/*! Ahimsa Design System v3.1.0 — Day + Night
 *  MIT. Fonts are SIL OFL-1.1; ship fonts/OFL-*.txt alongside them.
 *  https://ahimsa-design.riverma.com
 *  Generated by tools/build.mjs — edit src/, not this file. */

/* ===== fonts/day.css ===== */
/* Ahimsa Day webfonts — self-hosted, SIL Open Font License 1.1.
   License texts travel with the binaries: fonts/OFL-Fraunces.txt,
   fonts/OFL-DMSans.txt. Variable fonts, Latin subset. No network requests,
   ever — a Google Fonts @import would break every offline consumer, and
   would fail their build checks outright.

   Paths assume the fonts/ directory sits at the site root. If yours lives
   elsewhere, rewrite these four urls; CSS cannot interpolate a variable
   into url(). */

@font-face {
  font-family: "Fraunces";
  src: url("fonts/Fraunces-VariableFont.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Fraunces";
  src: url("fonts/Fraunces-Italic-VariableFont.woff2") format("woff2");
  font-weight: 100 900;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: "DM Sans";
  src: url("fonts/DMSans-VariableFont.woff2") format("woff2");
  font-weight: 100 1000;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "DM Sans";
  src: url("fonts/DMSans-Italic-VariableFont.woff2") format("woff2");
  font-weight: 100 1000;
  font-style: italic;
  font-display: swap;
}

/* ===== tokens/shared.css ===== */
/* Ahimsa — shared tokens.
   Geometry and time. These do NOT change between Day and Night: switching
   aesthetic changes color, type voice and surface character, never the
   grid, the corners or the timing. That stability is why both themes read
   as one system.

   Sizes are in rem, at the same computed values the system has always
   rendered (13px = 0.8125rem at a 16px root). px would silently override
   a person's own browser font-size setting, which is the opposite of what
   this system is for. See docs/accessibility. */

:root {
  /* ---- Spacing — 4px grid. Generous negative space is a core principle;
     err toward the larger step. If a section feels crowded, remove
     elements before shrinking type. ---- */
  --space-0:  0px;
  --space-1:  4px;
  --space-2:  8px;
  --space-3:  12px;
  --space-4:  16px;
  --space-5:  20px;
  --space-6:  24px;
  --space-7:  28px;
  --space-8:  32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-16: 64px;
  --space-20: 80px;
  --space-24: 96px;

  /* ---- Radius — rounded at every scale, never sharp. Sharp corners in UI
     carry an implicit threat; rounded corners carry receptiveness. This is
     a system law, not a Day preference. ---- */
  --radius-xs:   4px;    /* small pill-adjacent elements */
  --radius-sm:   8px;    /* small buttons, tags */
  --radius-md:   12px;   /* standard inputs, small cards */
  --radius-lg:   16px;   /* cards */
  --radius-xl:   20px;   /* featured cards */
  --radius-2xl:  24px;   /* bottom sheets, panels */
  --radius-3xl:  32px;   /* large frames, phone-inset outer */
  --radius-full: 9999px; /* pills, monograms, circular buttons */

  /* ---- Type sizes — a musical, deliberately-uneven scale. Note there is
     no 12px or 14px step; the gaps are the point. ---- */
  --size-micro: 0.5625rem; /* 9px  */
  --size-xs:    0.625rem;  /* 10px */
  --size-sm:    0.6875rem; /* 11px */
  --size-base:  0.8125rem; /* 13px — a LABEL size, not a reading size */
  --size-md:    0.9375rem; /* 15px */
  --size-lg:    1.0625rem; /* 17px */
  --size-xl:    1.375rem;  /* 22px */
  --size-2xl:   1.75rem;   /* 28px */
  --size-3xl:   2.125rem;  /* 34px */
  --size-4xl:   2.5rem;    /* 40px */
  --size-5xl:   3rem;      /* 48px */

  /* Running prose uses this, always. --size-base is for UI labels. */
  --size-reading: 1rem;    /* 16px */

  /* Fluid display, for editorial moments that want to fill a viewport.
     Both themes; the tracking differs (Day pulls tight, Night opens up). */
  --size-fluid-display: clamp(2.75rem, 9vw, 7.5rem);
  --size-fluid-heading: clamp(1.75rem, 4.5vw, 3.25rem);

  /* ---- Weights ---- */
  --weight-extralight: 200;
  --weight-light:      300;
  --weight-regular:    400;
  --weight-medium:     500;
  --weight-semibold:   600;
  --weight-bold:       700;

  /* ---- Line heights ---- */
  --leading-tight:   1.0;  /* large display type */
  --leading-snug:    1.15; /* headings */
  --leading-base:    1.4;  /* body copy */
  --leading-relaxed: 1.55; /* long reads */

  /* ---- Letter spacing. Day pulls display tight and opens small caps
     wide; Night inverts the display end. ---- */
  --tracking-tightest: -0.02em;  /* very large display */
  --tracking-tight:    -0.01em;  /* serif headings */
  --tracking-normal:   0;
  --tracking-wide:     0.15em;   /* small caps utility labels */
  --tracking-wider:    0.22em;   /* standard small caps */
  --tracking-widest:   0.32em;   /* very quiet meta */
  --tracking-letter:   0.4em;    /* sparse — dividers, section labels */

  /* ---- Layout ---- */
  --gutter:       20px;
  --gutter-fluid: clamp(20px, 5.2vw, 106px); /* opt-in editorial mode */

  /* ---- Motion — gentle and decelerating. Nothing snaps, nothing
     overshoots. Motion mimics settling, not predator movement. Every
     easing below ends at 1.0, with no value above it. ---- */
  --duration-instant: 100ms;
  --duration-fast:    200ms;
  --duration-base:    300ms;
  --duration-slow:    400ms;
  --duration-slower:  550ms; /* sheet slide-ups, card flips */
  --duration-slowest: 800ms; /* onboarding transitions */
  --duration-reveal:  620ms; /* clip-path reveals of large surfaces */

  --ease-standard:   cubic-bezier(0.4, 0.0, 0.2, 1.0);    /* small state changes */
  --ease-emphasized: cubic-bezier(0.32, 0.72, 0.32, 1.0); /* carousels, card reveals, sheets */
  --ease-decelerate: cubic-bezier(0.0, 0.0, 0.2, 1.0);    /* entering elements */
  --ease-accelerate: cubic-bezier(0.4, 0.0, 1.0, 1.0);    /* exiting elements */
  --ease-reveal:     cubic-bezier(0.76, 0, 0.24, 1);      /* clip-path wipes */
  --ease-expo:       cubic-bezier(0.22, 1, 0.36, 1);      /* long, soft settle */

  /* ---- The growing underline (.ah-link) ---- */
  --underline-thickness: max(1.5px, 0.045em);
  --underline-gap:       0.22em;
  --underline-duration:  220ms;
  --underline-ease:      cubic-bezier(0.65, 0, 0.35, 1);
}

/* ===== tokens/day/palette.css ===== */
/* Ahimsa Day — raw palette.
   Warm paper and cocoa ink under a light gray, clouded sky, with accents that carry a
   little more life than the newsprint they replace. These are raw values;
   components must reference the semantic aliases in semantic.css, never a
   step from this file.

   Never pure white, never pure black. There is intentionally NO bright-red
   danger value anywhere in this palette — errors use warm clay.

   Each accent family is anchored on its -600 step (the chosen color, which
   clears 4.5:1 on a card). Lighter steps are tints toward paper; -700 is a
   shade toward the darkest ink. */

.ahimsa-day {
  /* ---- Neutral: paper -> ink. Warm, against the gray sky. The workhorse. ---- */
  --neutral-50: #f7f6f1; /* chalk — elevated surfaces, sheets, cards */
  --neutral-100: #f1eee6; /* inline elevated blocks */
  --neutral-200: #ece6dc; /* canvas — the flat fallback under the sky */
  --neutral-300: #e0dbd1; /* sunk / recessed surfaces */
  --neutral-400: #b5aea6; /* disabled state, faint text */
  --neutral-500: #8a8179; /* muted mid-tone */
  --neutral-600: #5e5149; /* secondary text */
  --neutral-700: #3a2c25; /* body text */
  --neutral-800: #30221c; /* cocoa — heading text */
  --neutral-900: #1c1410; /* inverse surface */

  /* ================================================================
     The ahimsa-tradition accents — the recommended family.
     Named for the tradition the system takes its ethics from:
     saffron (renunciate robes), turmeric (ritual gold), clay (earthen
     lamps), bodhi (the fig tree of awakening), peacock (India's bird),
     indigo (the Champaran satyagraha crop), monsoon (gray-blue rain
     skies). Nothing high-chroma, nothing "poppy".
     ================================================================ */

  --saffron-300: #e0c0a3;
  --saffron-400: #d4a278;
  --saffron-500: #c37a40;
  --saffron-600: #b4560d; /* Marigold Robe — the saffron of renunciate robes */
  --saffron-700: #80400e;

  --turmeric-300: #d4c69f;
  --turmeric-400: #c0ad72;
  --turmeric-500: #a58b36;
  --turmeric-600: #8c6c00; /* Gold Leaf — ritual gold */
  --turmeric-700: #664e05;

  --clay-300: #ddbead;
  --clay-400: #ce9f89;
  --clay-500: #ba7659;
  --clay-600: #a8502e; /* Kiln — fired-earth terracotta */
  --clay-700: #783c24;

  --bodhi-300: #bfccad;
  --bodhi-400: #9db789;
  --bodhi-500: #709a5a;
  --bodhi-600: #46802f; /* Young Leaf — the new growth of the awakening tree */
  --bodhi-700: #385b24;

  --peacock-300: #adcbc2;
  --peacock-400: #7fb4ac;
  --peacock-500: #42968f;
  --peacock-600: #0a7a75; /* Verdigris — the primary peacock */
  --peacock-700: #105753;

  --indigo-300: #c0becf;
  --indigo-400: #9f9ec2;
  --indigo-500: #7375b0;
  --indigo-600: #4a4fa0; /* Champaran — the satyagraha crop */
  --indigo-700: #3a3b6f;

  --monsoon-300: #c4c5c4;
  --monsoon-400: #a5aaaf;
  --monsoon-500: #7c8793;
  --monsoon-600: #56677a; /* Rain Cloud — monsoon skies */
  --monsoon-700: #424b56;

  /* ================================================================
     The classic accents — the alternate family.
     These are the original Ahimsa accents, kept because a consumer built
     a full set of person-gradients directly on them. Same muted, warm-
     adjusted register; reach for the tradition set first.

     NOTE the one rename: this family's blue-violet is --periwinkle-*.
     It was historically called --indigo-* in one consumer, which
     collided with the tradition set's darker indigo at a different
     value. Tradition indigo keeps the name; this one moved.
     ================================================================ */

  --amber-300: #d2c3b4;
  --amber-400: #c3b1a1;
  --amber-500: #b2a496;
  --amber-600: #97897b;
  --amber-700: #786a5d;

  --rose-300: #e0d1d1;
  --rose-400: #d1bbbd;
  --rose-500: #bfa6a9;
  --rose-600: #a2888c;
  --rose-700: #826b6f;

  --terracotta-300: #bfb2ac;
  --terracotta-400: #ac9b94;
  --terracotta-500: #97847a;
  --terracotta-600: #7a695f;
  --terracotta-700: #5f5148;

  --sage-300: #bfc7c2;
  --sage-400: #a2ada4;
  --sage-500: #849386;
  --sage-600: #6a796d;
  --sage-700: #515f54;

  --violet-300: #cec7d5;
  --violet-400: #bcb4c6;
  --violet-500: #a9a1b6;
  --violet-600: #8f879d;
  --violet-700: #746c81;

  --slate-300: #afb5bd;
  --slate-400: #929ba7;
  --slate-500: #788392;
  --slate-600: #5d6977;
  --slate-700: #49525e;

  --periwinkle-300: #bec0d1;
  --periwinkle-400: #9a9fba;
  --periwinkle-500: #7d82a0;
  --periwinkle-600: #656b88;
  --periwinkle-700: #515571;
}

/* ===== tokens/day/gradients.css ===== */
/* Ahimsa Day — signature gradients.
   All 155 degrees (cream is 170 for large horizontal surfaces), stops
   always at 0 / 30 / 60 / 100%. Named by feeling.

   Day is a light gray sky with soft white clouds. --gradient-sky is the
   sky (a quiet gray, a touch deeper overhead) and --ambient-clouds lays the
   clouds over it. Together they are .ah-sky. Everything else is a moment of
   emphasis.

   The rule: ONE GRADIENT OWNS A SCREEN. The sky counts as the ground, not
   as a second gradient: never put two emphatic gradients side by side.

   No repeating patterns, no textures, no photography direction. */

.ahimsa-day {
  /* ---- The sky. Lit from above, so it runs top to bottom: 180 degrees,
     the one exception to 155 besides cream. Neutral gray, so the white of
     the clouds is the only light in it. ---- */
  --gradient-sky:   linear-gradient(180deg, #dcdcdc 0%, #e3e3e3 30%, #e9e9e9 60%, #efefef 100%);

  /* ---- The warm haze. The sky before 3.1; still a surface of its own. ---- */
  --gradient-dawn:  linear-gradient(155deg, #fbf3e6 0%, #f3ece2 30%, #e9e6e0 60%, #dfe1e0 100%);
  --gradient-cream: linear-gradient(170deg, #fcf8ef 0%, #f4eee4 50%, #e7e3dc 100%);

  /* DEPRECATED in 3.1: .ah-sky no longer uses it, and 4.0 removes it.
     The one radial in Day, a warm light that falls away to nothing before
     it reaches the middle of the screen. It was the 3.0 sky's sun, a layer
     over --gradient-dawn, never a surface of its own. */
  --gradient-sun: radial-gradient(60% 55% at 14% -4%, #fff0b0e6 0%, #fff0b000 70%);

  /* ---- The ahimsa-tradition set ---- */
  --gradient-saffron:   linear-gradient(155deg, #c37a40 0%, #d4a278 30%, #e0c0a3 60%, #eddcca 100%);
  --gradient-turmeric:  linear-gradient(155deg, #a58b36 0%, #c0ad72 30%, #d4c69f 60%, #e7dec8 100%);
  --gradient-clay:      linear-gradient(155deg, #ba7659 0%, #ce9f89 30%, #ddbead 60%, #ebdbcf 100%);
  --gradient-bodhi:     linear-gradient(155deg, #709a5a 0%, #9db789 30%, #bfccad 60%, #dee1cf 100%);
  --gradient-peacock:   linear-gradient(155deg, #42968f 0%, #7fb4ac 30%, #adcbc2 60%, #d6e0d8 100%);
  --gradient-indigo:    linear-gradient(155deg, #7375b0 0%, #9f9ec2 30%, #c0becf 60%, #dedbde 100%);
  --gradient-monsoon:   linear-gradient(155deg, #7c8793 0%, #a5aaaf 30%, #c4c5c4 60%, #e0ded9 100%);

  /* ---- The classic set ---- */
  --gradient-amber:            linear-gradient(155deg, #b2a496 0%, #c1b5a7 30%, #d0c6b8 60%, #ded6ca 100%);
  --gradient-rose:             linear-gradient(155deg, #c0a5a8 0%, #d2babc 30%, #e1d0d1 60%, #eee5e5 100%);
  --gradient-coral:            linear-gradient(155deg, #bca096 0%, #c9b1a8 30%, #d6c4bb 60%, #e4d7d1 100%);
  --gradient-terracotta:       linear-gradient(155deg, #97847a 0%, #ac9b94 30%, #bfb2ac 60%, #d2c9c4 100%);
  --gradient-sage:             linear-gradient(155deg, #869188 0%, #a3aca5 30%, #c0c6c2 60%, #dbdfdc 100%);
  --gradient-teal:             linear-gradient(155deg, #7d9893 0%, #9bafac 30%, #b9c7c4 60%, #d7dfdd 100%);
  --gradient-violet:           linear-gradient(155deg, #aaa4b3 0%, #bcb7c3 30%, #cec9d3 60%, #dfdce2 100%);
  --gradient-slate:            linear-gradient(155deg, #7c848e 0%, #959ba4 30%, #b1b5bb 60%, #ccced1 100%);
  --gradient-periwinkle:       linear-gradient(155deg, #82869b 0%, #9fa2b5 30%, #c0c2cf 60%, #d7d5dd 100%);
  --gradient-periwinkle-soft:  linear-gradient(155deg, #82869b 0%, #9fa2b5 30%, #bbbecb 60%, #d4d5dd 100%);

  /* ---- Gradient-only hues. No 5-step ramp was ever authored for these,
     and Ahimsa's gradients are independently designed rather than derived
     from the palettes, so inventing ramps here would be a fabrication.
     Use them as surfaces, not as ink. ---- */
  --gradient-mauve: linear-gradient(155deg, #aa939d 0%, #beaab3 30%, #d4c6cb 60%, #e9e0e2 100%);
  --gradient-olive: linear-gradient(155deg, #7e8676 0%, #989f91 30%, #afb5a9 60%, #c4c7be 100%);
}

/* ===== tokens/day/semantic.css ===== */
/* Ahimsa Day — semantic tokens.
   The ONLY color tokens a component may reference. Both themes define
   this exact set of names, which is what makes markup theme-portable. */

.ahimsa-day {
  /* ---- Surfaces ---- */
  --surface-canvas:      var(--neutral-200); /* flat fallback; the sky (.ah-sky) sits over it */
  --surface-canvas-flat: #ece6dc;            /* for html/body, where a gradient can't go */
  --surface-elevated:    var(--neutral-50);  /* cards, sheets, elevated surfaces */
  --surface-sunk:        var(--neutral-300); /* recessed surfaces, subtle wells */
  --surface-inverse:     var(--neutral-800); /* dark surface, used sparingly */

  /* ---- Text. Quiet steps are the cocoa at low alpha rather than a
     gray, so nothing ever reads cold against the paper.

     DEVIATION FROM THE ORIGINAL TOKEN SET, and the only one in Day.
     The original alphas were 65 / 45 / 28%, which measure 3.86 / 2.39 /
     1.66 against the darkest Day ground — secondary fails AA outright and
     muted fails even the 3:1 large-text floor. They are not usable for
     descriptions or labels, which is what they are for.

     The values below are 76 / 60 / 40%, which measure 5.15 / 3.40 / 2.14.
     They are not invented: a shipping consumer independently derived
     exactly these, for a different reason (quiet text fading on a warm
     gradient canvas), and they turn out to be the accessible ones.
     tools/check-parity.mjs records all three as named deviations, so the
     gate still catches any change nobody decided on. ---- */
  --text-heading:   var(--neutral-800); /* titles, display type */
  --text-body:      var(--neutral-700); /* body copy */
  --text-secondary: #3a2c25c2;          /* 76% — descriptions, captions. 5.15:1 worst case */
  --text-muted:     #3a2c2599; /* NON-TEXT ONLY — status dots, rules, disabled marks.
                                   Measures 3.40:1, which clears 3:1 for non-text
                                   (1.4.11) but NOT the 4.5:1 small text owes. No component
                                   sets type in this. */
  --text-faint:     #3a2c2566;          /* 40% — hints ONLY. 2.14:1: never the sole carrier of anything */
  --text-inverse:   var(--neutral-50);  /* text on dark surfaces */

  /* ---- Borders. Mostly absent by design: inputs use a single bottom rule
     rather than a box, and separators are cocoa at low alpha. ---- */
  --border-subtle: #3a2c2514; /* ~8%  — barely-there divisions */
  --border-medium: #3a2c252e; /* ~18% — clear separators */
  --border-strong: #3a2c2559; /* ~35% — emphatic, but DECORATIVE only: 1.92:1 */

  /* The boundary of an interactive control — a ghost button's ring, an
     outline pill. That line IS the control's edge, so it owes 3:1
     (WCAG 1.4.11) in a way a separator does not. --border-strong cannot
     carry it. Additive; nothing upstream had this. */
  --border-control: #3a2c2599; /* 60% — 3.40:1 worst case */

  /* ---- Accents ---- */
  --accent-primary:   var(--peacock-600); /* brand — Verdigris, the one hint of color */
  --accent-secondary: var(--indigo-600);  /* interactive — links, active tabs */
  --accent-success:   var(--bodhi-600);
  --accent-warning:   var(--clay-600);    /* the ONLY error color. Warm, not alarming.
                                             There is no red token in this system. */

  --accent-warning-surface: var(--clay-600); /* fill for a destructive control — fired earth, not red.
                                      clay-500 was the inherited choice and puts the label at
                                      4.08:1; -600 reaches 5.67:1 and is no more alarming.
                                      A destructive action is still not an emergency. */

  /* Setting SMALL TEXT in an accent? Use the -700 step, not these.
     The -600 accents clear 4.5:1 on a card but fall short on the shaded end
     of the sky, and --accent-success and --accent-warning are tuned as
     fills and marks (3:1 non-text). -700 clears small text everywhere. */

  /* ---- Focus. Must clear 3:1 against both the element and its ground
     (WCAG 1.4.11). Never suppressed — see base.css. ---- */
  --focus-ring:        var(--peacock-700);
  --focus-ring-offset: var(--surface-canvas-flat);
  --focus-ring-width:  2px;

  /* ---- Glass. Needs backdrop-filter to read correctly. Reserved for
     surfaces sitting on a gradient, and for the sheet scrim. Not used
     decoratively on flat paper. ---- */
  --glass-overlay:        #ffffff6b; /* 42% white — frosted glass surfaces */
  --glass-overlay-strong: #ffffff8f; /* 56% white — more opaque frosted panels */
  --glass-scrim:          #1c141073; /* 45% dark — modal / sheet scrims */
  --glass-blur:           12px;

  /* ---- Glow. Day has none: depth here comes from warm paper shadow.
     Declared so components can reference it unconditionally. ---- */
  --glow-sm: none;
  --glow-md: none;
  --glow-lg: none;

  /* ---- Day's display type pulls tight. Night opens up instead. ---- */
  --tracking-display: -0.045em;
}

/* ===== tokens/day/elevation.css ===== */
/* Ahimsa Day — elevation.
   Cocoa-tinted, low-opacity, layered. Never harsh, never black
   (except the one signature deep drop). --shadow-md carries an inner white
   highlight, which is what makes a card read as paper rather than as a
   floating rectangle. */

.ahimsa-day {
  --shadow-xs:    0 1px 2px #3a2c2514;
  --shadow-sm:    0 2px 8px #3a2c2521;
  --shadow-md:    0 4px 20px #3a2c2529, inset 0 1px 0 #ffffff99;
  --shadow-lg:    0 8px 32px #3a2c2533;
  --shadow-xl:    0 30px 60px -20px #00000059, 0 12px 24px -12px #00000033;
  --shadow-sheet: 0 -10px 40px #0000002e;
}

/* ===== tokens/day/type.css ===== */
/* Ahimsa Day — typography.
   Two families doing different jobs. Fraunces (variable serif) carries
   emotional weight: display type, titles, italic pull quotes. DM Sans
   handles utility: labels, small caps, buttons, dates.

   Two signature styles define the fingerprint:
     .ah-small-caps  — wide-tracked uppercase sans, the utility voice
     .ah-pull-quote  — italic serif, the emotional voice

   The split IS the tone. An emotional line goes in italics; a factual line
   stays upright. Never put quotation marks on a pull quote — the italic
   styling is already doing that job. */

.ahimsa-day {
  --font-display: "Fraunces", Georgia, "Iowan Old Style", serif;
  --font-body:    "DM Sans", -apple-system, system-ui, sans-serif;
  --font-mono:    ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}

.ahimsa-day .ah-display-xl { font-family: var(--font-display); font-size: var(--size-5xl);   font-weight: 400; line-height: 1.0;  letter-spacing: -0.015em; }
.ahimsa-day .ah-display-l  { font-family: var(--font-display); font-size: var(--size-4xl);   font-weight: 400; line-height: 1.0;  letter-spacing: -0.015em; }
.ahimsa-day .ah-display-m  { font-family: var(--font-display); font-size: var(--size-3xl);   font-weight: 400; line-height: 1.05; letter-spacing: -0.01em; }
.ahimsa-day .ah-heading-l  { font-family: var(--font-display); font-size: var(--size-2xl);   font-weight: 400; line-height: 1.1;  letter-spacing: -0.01em; }
.ahimsa-day .ah-heading-m  { font-family: var(--font-display); font-size: var(--size-xl);    font-weight: 400; line-height: 1.15; }
.ahimsa-day .ah-title-l    { font-family: var(--font-display); font-size: var(--size-lg);    font-weight: 400; line-height: 1.2; }
.ahimsa-day .ah-title-m    { font-family: var(--font-display); font-size: var(--size-md);    font-weight: 400; line-height: 1.2; }
.ahimsa-day .ah-body       { font-family: var(--font-body);    font-size: var(--size-base);  font-weight: 400; line-height: 1.4; }
.ahimsa-day .ah-body-serif { font-family: var(--font-display); font-size: var(--size-md);    font-weight: 400; line-height: 1.55; }
.ahimsa-day .ah-caption    { font-family: var(--font-display); font-size: var(--size-sm);    font-weight: 300; line-height: 1.4;  font-style: italic; }
.ahimsa-day .ah-pull-quote { font-family: var(--font-display); font-size: var(--size-lg);    font-weight: 400; line-height: 1.35; font-style: italic; }
.ahimsa-day .ah-label      { font-family: var(--font-body);    font-size: var(--size-sm);    font-weight: 500; line-height: 1.2; }
.ahimsa-day .ah-small-caps { font-family: var(--font-body);    font-size: var(--size-xs);    font-weight: 500; line-height: 1.2;  letter-spacing: 0.22em; text-transform: uppercase; }
.ahimsa-day .ah-micro-caps { font-family: var(--font-body);    font-size: var(--size-micro); font-weight: 500; line-height: 1.2;  letter-spacing: 0.32em; text-transform: uppercase; }

/* The editorial moment: display type that fills a viewport, pulled tight.
   Day's display tracking is negative — the letters lean into each other. */
.ahimsa-day .ah-display-fluid {
  font-family: var(--font-display);
  font-size: var(--size-fluid-display);
  font-weight: 400;
  line-height: 0.92;
  letter-spacing: var(--tracking-display);
  text-wrap: balance;
}

/* Running prose. Mandatory for anything anyone actually reads: --size-base
   is a label size and always was. */
.ahimsa-day .ah-prose {
  font-family: var(--font-display);
  font-size: var(--size-reading);
  font-weight: 400;
  line-height: var(--leading-relaxed);
  text-wrap: pretty;
}

/* ===== tokens/day/surfaces.css ===== */
/* Ahimsa Day — gradient surfaces. GENERATED by tools/build-surfaces.mjs.
   Do not edit. Change the gradients or the generator instead.

   Each class sets a gradient background AND the ink that is measured to
   work on it. Put one on any element and its contents read correctly:

     <section class="ah-surface--dawn"> ... </section>

   Scoped to the theme, because a Day gradient and a Night gradient need
   different ink and two of them once shared a name. To show a Day surface
   inside a Night page (a specimen, say), wrap it: <div class="ahimsa-day">.

   ONE GRADIENT OWNS A SCREEN. Two of these side by side is the rule this
   system breaks least gladly. */

/* sky — worst stop #dcdcdc; heading 13.24:1, body/secondary 4.52:1, muted 3.00:1 */
.ahimsa-day .ah-surface--sky {
  background: var(--gradient-sky);
  --text-heading:   #1c1410;
  --text-body:      #1c14109e;
  --text-secondary: #1c14109e;
  --text-muted:     #1c14107a;
  --border-control: #1c141094;
}

/* dawn — worst stop #dfe1e0; heading 13.82:1, body/secondary 4.54:1, muted 3.00:1 */
.ahimsa-day .ah-surface--dawn {
  background: var(--gradient-dawn);
  --text-heading:   #1c1410;
  --text-body:      #1c14109d;
  --text-secondary: #1c14109d;
  --text-muted:     #1c141079;
  --border-control: #1c141093;
}

/* cream — worst stop #e7e3dc; heading 14.19:1, body/secondary 4.53:1, muted 3.02:1 */
.ahimsa-day .ah-surface--cream {
  background: var(--gradient-cream);
  --text-heading:   #1c1410;
  --text-body:      #1c14109c;
  --text-secondary: #1c14109c;
  --text-muted:     #1c141079;
  --border-control: #1c141093;
}

/* sun — worst stop #fff0b0; heading 15.85:1, body/secondary 4.54:1, muted 3.02:1 */
.ahimsa-day .ah-surface--sun {
  background: var(--gradient-sun);
  --text-heading:   #1c1410;
  --text-body:      #1c141099;
  --text-secondary: #1c141099;
  --text-muted:     #1c141077;
  --border-control: #1c141091;
}

/* saffron — worst stop #c37a40; heading 5.34:1, body/secondary 4.53:1, muted 3.01:1 */
.ahimsa-day .ah-surface--saffron {
  background: var(--gradient-saffron);
  --text-heading:   #1c1410;
  --text-body:      #1c1410de;
  --text-secondary: #1c1410de;
  --text-muted:     #1c1410a1;
  --border-control: #1c1410bb;
}

/* turmeric — worst stop #a58b36; heading 5.49:1, body/secondary 4.52:1, muted 3.01:1 */
.ahimsa-day .ah-surface--turmeric {
  background: var(--gradient-turmeric);
  --text-heading:   #1c1410;
  --text-body:      #1c1410da;
  --text-secondary: #1c1410da;
  --text-muted:     #1c14109f;
  --border-control: #1c1410b9;
}

/* clay — worst stop #ba7659; heading 5.03:1, body/secondary 4.51:1, muted 3.01:1 */
.ahimsa-day .ah-surface--clay {
  background: var(--gradient-clay);
  --text-heading:   #1c1410;
  --text-body:      #1c1410e7;
  --text-secondary: #1c1410e7;
  --text-muted:     #1c1410a6;
  --border-control: #1c1410c0;
}

/* bodhi — worst stop #709a5a; heading 5.58:1, body/secondary 4.52:1, muted 3.01:1 */
.ahimsa-day .ah-surface--bodhi {
  background: var(--gradient-bodhi);
  --text-heading:   #1c1410;
  --text-body:      #1c1410d7;
  --text-secondary: #1c1410d7;
  --text-muted:     #1c14109d;
  --border-control: #1c1410b7;
}

/* peacock — worst stop #42968f; heading 5.18:1, body/secondary 4.52:1, muted 3.00:1 */
.ahimsa-day .ah-surface--peacock {
  background: var(--gradient-peacock);
  --text-heading:   #1c1410;
  --text-body:      #1c1410e1;
  --text-secondary: #1c1410e1;
  --text-muted:     #1c1410a1;
  --border-control: #1c1410bb;
}

/* indigo — SURFACE ONLY. Stop #7375b0 cannot carry AA text at any
   opacity (best possible 4.24:1, with the most present ink the
   system allows). Use it as a backdrop and put text in an opaque .ah-card. */
.ahimsa-day .ah-surface--indigo { background: var(--gradient-indigo); }

/* monsoon — worst stop #7c8793; heading 4.96:1, body/secondary 4.52:1, muted 3.01:1 */
.ahimsa-day .ah-surface--monsoon {
  background: var(--gradient-monsoon);
  --text-heading:   #1c1410;
  --text-body:      #1c1410ea;
  --text-secondary: #1c1410ea;
  --text-muted:     #1c1410a7;
  --border-control: #1c1410c1;
}

/* amber — worst stop #b2a496; heading 7.47:1, body/secondary 4.53:1, muted 3.01:1 */
.ahimsa-day .ah-surface--amber {
  background: var(--gradient-amber);
  --text-heading:   #1c1410;
  --text-body:      #1c1410bb;
  --text-secondary: #1c1410bb;
  --text-muted:     #1c14108d;
  --border-control: #1c1410a7;
}

/* rose — worst stop #c0a5a8; heading 7.94:1, body/secondary 4.50:1, muted 3.00:1 */
.ahimsa-day .ah-surface--rose {
  background: var(--gradient-rose);
  --text-heading:   #1c1410;
  --text-body:      #1c1410b6;
  --text-secondary: #1c1410b6;
  --text-muted:     #1c14108a;
  --border-control: #1c1410a3;
}

/* coral — worst stop #bca096; heading 7.44:1, body/secondary 4.51:1, muted 3.00:1 */
.ahimsa-day .ah-surface--coral {
  background: var(--gradient-coral);
  --text-heading:   #1c1410;
  --text-body:      #1c1410bb;
  --text-secondary: #1c1410bb;
  --text-muted:     #1c14108d;
  --border-control: #1c1410a7;
}

/* terracotta — worst stop #97847a; heading 5.10:1, body/secondary 4.51:1, muted 3.01:1 */
.ahimsa-day .ah-surface--terracotta {
  background: var(--gradient-terracotta);
  --text-heading:   #1c1410;
  --text-body:      #1c1410e5;
  --text-secondary: #1c1410e5;
  --text-muted:     #1c1410a5;
  --border-control: #1c1410bf;
}

/* sage — worst stop #869188; heading 5.55:1, body/secondary 4.51:1, muted 3.01:1 */
.ahimsa-day .ah-surface--sage {
  background: var(--gradient-sage);
  --text-heading:   #1c1410;
  --text-body:      #1c1410d8;
  --text-secondary: #1c1410d8;
  --text-muted:     #1c14109e;
  --border-control: #1c1410b8;
}

/* teal — worst stop #7d9893; heading 5.86:1, body/secondary 4.52:1, muted 3.01:1 */
.ahimsa-day .ah-surface--teal {
  background: var(--gradient-teal);
  --text-heading:   #1c1410;
  --text-body:      #1c1410d1;
  --text-secondary: #1c1410d1;
  --text-muted:     #1c14109a;
  --border-control: #1c1410b3;
}

/* violet — worst stop #aaa4b3; heading 7.49:1, body/secondary 4.50:1, muted 3.02:1 */
.ahimsa-day .ah-surface--violet {
  background: var(--gradient-violet);
  --text-heading:   #1c1410;
  --text-body:      #1c1410ba;
  --text-secondary: #1c1410ba;
  --text-muted:     #1c14108d;
  --border-control: #1c1410a7;
}

/* slate — worst stop #7c848e; heading 4.80:1, body/secondary 4.50:1, muted 3.01:1 */
.ahimsa-day .ah-surface--slate {
  background: var(--gradient-slate);
  --text-heading:   #1c1410;
  --text-body:      #1c1410f0;
  --text-secondary: #1c1410f0;
  --text-muted:     #1c1410aa;
  --border-control: #1c1410c3;
}

/* periwinkle — worst stop #82869b; heading 5.04:1, body/secondary 4.51:1, muted 3.02:1 */
.ahimsa-day .ah-surface--periwinkle {
  background: var(--gradient-periwinkle);
  --text-heading:   #1c1410;
  --text-body:      #1c1410e7;
  --text-secondary: #1c1410e7;
  --text-muted:     #1c1410a6;
  --border-control: #1c1410c0;
}

/* periwinkle-soft — worst stop #82869b; heading 5.04:1, body/secondary 4.51:1, muted 3.02:1 */
.ahimsa-day .ah-surface--periwinkle-soft {
  background: var(--gradient-periwinkle-soft);
  --text-heading:   #1c1410;
  --text-body:      #1c1410e7;
  --text-secondary: #1c1410e7;
  --text-muted:     #1c1410a6;
  --border-control: #1c1410c0;
}

/* mauve — worst stop #aa939d; heading 6.37:1, body/secondary 4.50:1, muted 3.00:1 */
.ahimsa-day .ah-surface--mauve {
  background: var(--gradient-mauve);
  --text-heading:   #1c1410;
  --text-body:      #1c1410c8;
  --text-secondary: #1c1410c8;
  --text-muted:     #1c141095;
  --border-control: #1c1410af;
}

/* olive — worst stop #7e8676; heading 4.80:1, body/secondary 4.51:1, muted 3.01:1 */
.ahimsa-day .ah-surface--olive {
  background: var(--gradient-olive);
  --text-heading:   #1c1410;
  --text-body:      #1c1410f0;
  --text-secondary: #1c1410f0;
  --text-muted:     #1c1410aa;
  --border-control: #1c1410c3;
}

/* ===== tokens/night/palette.css ===== */
/* Ahimsa Night — raw palette.
   Night is the same family as Day, lit from the other side. Every accent
   keeps the hue Day chose and gains a lighter step, because a dark ground
   needs brighter ink than paper does. Frost is Night's own: pale ice, the
   core of every glow.

   Ahimsa's color law holds without exception: NEVER PURE BLACK, NEVER
   PURE WHITE. The deepest ground here is #060914 and the lightest ink
   #e4eef5. And as in Day, there is no red.

   Each accent family is anchored on its -300 step (the chosen color).
   -200 is a tint toward ice; -400 to -700 sink toward the ground. */

.ahimsa-night {
  /* ---- Neutral: ice -> deep midnight. 50 is still "lightest"; it is
     the semantic layer that flips which end is ground and which is ink. ---- */
  --neutral-50:  #e4eef5; /* heading ink — frost */
  --neutral-100: #d3dfe9; /* body ink */
  --neutral-200: #b0bfce;
  --neutral-300: #8a9bb0;
  --neutral-400: #667790; /* disabled state, faint text */
  --neutral-500: #495771;
  --neutral-600: #323f58;
  --neutral-700: #1e2940; /* hairline-adjacent wells */
  --neutral-800: #0f1626; /* elevated surface — deep well */
  --neutral-900: #0a1124; /* canvas */
  --neutral-950: #060914; /* sunk — the deepest ground in the system */

  /* ---- The tradition accents, in Night's register. ---- */
  --saffron-200: #ecd4b3;
  --saffron-300: #e8b878; /* Ghee */
  --saffron-400: #c09a69;
  --saffron-500: #9d7f5b;
  --saffron-600: #79654e;
  --saffron-700: #5a4d42;

  --turmeric-200: #eee3b5;
  --turmeric-300: #ecd47a; /* Gold Dust */
  --turmeric-400: #c3b16b;
  --turmeric-500: #9f925d;
  --turmeric-600: #7b734f;
  --turmeric-700: #5b5743;

  --clay-200: #e9c7bb;
  --clay-300: #e3a086; /* Low Embers */
  --clay-400: #bc8674;
  --clay-500: #996f65;
  --clay-600: #775955;
  --clay-700: #584447;

  --bodhi-200: #b2d9c0;
  --bodhi-300: #7ec18f; /* Fig Leaf */
  --bodhi-400: #69a17c;
  --bodhi-500: #57856b;
  --bodhi-600: #44695a;
  --bodhi-700: #34504b;

  --peacock-200: #b2e1e8;
  --peacock-300: #7fd0d8; /* Glacier Lake */
  --peacock-400: #6aaeb8;
  --peacock-500: #578f9b;
  --peacock-600: #45717e;
  --peacock-700: #345665;

  --indigo-200: #c4cbf8;
  --indigo-300: #a0a8f5; /* Nila */
  --indigo-400: #858dcf;
  --indigo-500: #6d75ae;
  --indigo-600: #555d8d;
  --indigo-700: #40476f;

  --monsoon-200: #c6d4e3;
  --monsoon-300: #a3b8cf; /* Rain Cloud */
  --monsoon-400: #879ab0;
  --monsoon-500: #6f7f95;
  --monsoon-600: #57657a;
  --monsoon-700: #414d62;

  /* ---- Frost — pale ice. Night's equivalent of cream: the quietest
     luminous register, and the core color of every glow. ---- */
  --frost-200: #e4f1f5;
  --frost-300: #c3e2ea;
  --frost-400: #9ccfdb;
  --frost-500: #74b8c8;
  --frost-600: #549cad;
  --frost-700: #3d7c8c;
}

/* ===== tokens/night/gradients.css ===== */
/* Ahimsa Night — atmospheric gradients.
   Still 155 degrees, still four stops, still ONE GRADIENT OWNS A SCREEN.
   Where Day's gradients are a warm wash, Night's climb out of the dark
   toward a single distant light.

   --gradient-midnight is the sky. Under it, .ah-ambient--stars lays a
   faint, static field of stars; nothing in it moves.

   Names are deliberately distinct from Day's, so that no palette-level
   name ever holds two different values across the system. */

.ahimsa-night {
  --gradient-deep:      linear-gradient(155deg, #060914 0%, #0a1124 50%, #0f1626 100%);
  --gradient-midnight:  linear-gradient(155deg, #060914 0%, #0a1124 30%, #101a35 60%, #182648 100%);
  --gradient-horizon:   linear-gradient(155deg, #0a1124 0%, #162436 30%, #284151 60%, #45717e 100%);
  --gradient-aurora:    linear-gradient(155deg, #0a1124 0%, #10243a 30%, #2a4656 60%, #45717e 100%);
  --gradient-iris:      linear-gradient(155deg, #0a1124 0%, #1b223b 30%, #363d61 60%, #555d8d 100%);
  --gradient-nightfall: linear-gradient(155deg, #0a1124 0%, #1b2337 30%, #374256 60%, #57657a 100%);
  --gradient-ember:     linear-gradient(155deg, #0a1124 0%, #22212f 30%, #493b40 60%, #775955 100%);

  /* The ground of the ambient layer: a field lit faintly from above,
     falling away to nothing at the edges. */
  --gradient-void: radial-gradient(120% 90% at 50% 0%, #182648 0%, #0a1124 55%, #060914 100%);
}

/* ===== tokens/night/semantic.css ===== */
/* Ahimsa Night — semantic tokens.
   Exactly the same names Day defines. A component never knows which theme
   it is in. */

.ahimsa-night {
  /* ---- Surfaces ---- */
  --surface-canvas:      var(--neutral-900);
  --surface-canvas-flat: #0a1124;
  --surface-elevated:    var(--neutral-800);
  --surface-sunk:        var(--neutral-950);
  --surface-inverse:     var(--neutral-50);

  /* ---- Text. Quiet steps sit HIGHER here than Day's 65/45/28% — low
     alpha ink disappears against a luminous ground. This is the same
     correction Giraffy already made in the other direction when it
     darkened its quiet text for a warm gradient canvas. ---- */
  --text-heading:   var(--neutral-50);
  --text-body:      var(--neutral-100);
  --text-secondary: #d3dfe9b8; /* ~72% */
  --text-muted:     #d3dfe980; /* NON-TEXT ONLY — status dots, rules, disabled marks.
                                   Measures 4.20:1, which clears 3:1 for non-text
                                   (1.4.11) but NOT the 4.5:1 small text owes. No component
                                   sets type in this. */
  --text-faint:     #d3dfe94d; /* ~30% */
  --text-inverse:   var(--neutral-900);

  /* ---- Borders. In Night these are the PRIMARY structural device —
     Day separates with shadow and paper, Night with a hairline.
     A hairline that carries meaning must clear 3:1 (WCAG 1.4.11); use
     --border-strong for those. Subtle and medium are decorative. ---- */
  --border-subtle: #d3dfe91a; /* ~10% — decorative only */
  --border-medium: #d3dfe933; /* ~20% — decorative only */
  --border-strong: #d3dfe959; /* ~35% — emphatic, but DECORATIVE only: 2.59:1 */

  /* The boundary of an interactive control. Owes 3:1 (WCAG 1.4.11). */
  --border-control: #d3dfe98c; /* 55% — 4.79:1 worst case */

  /* ---- Accents ---- */
  --accent-primary:   var(--peacock-300);
  --accent-secondary: var(--indigo-300);
  --accent-success:   var(--bodhi-300);
  --accent-warning:   var(--clay-300); /* the ONLY error color. Low Embers, never red. */

  --accent-warning-surface: var(--clay-400); /* fill for a destructive control — warm copper, not red.
                                      A destructive action is still not an emergency. */

  /* Setting SMALL TEXT in an accent? Use the -200 step, not these.
     --accent-success and --accent-warning are tuned as fills and marks and
     clear the 3:1 non-text bar; as 10px type they do not reach 4.5:1. */

  /* ---- Focus ---- */
  --focus-ring:        var(--peacock-300);
  --focus-ring-offset: var(--surface-canvas-flat);
  --focus-ring-width:  2px;

  /* ---- Glass. Tinted cool and much quieter than Day's white: a 42% white
     overlay on a dark ground reads as fog, not as glass. ---- */
  --glass-overlay:        #a9c6d914; /* ~8%  */
  --glass-overlay-strong: #a9c6d926; /* ~15% */
  --glass-scrim:          #05080ca6; /* ~65% — deeper than Day's 45% */
  --glass-blur:           14px;

  /* ---- Glow. Night's answer to Day's paper shadow. Emphasis here is
     light, not lift. ---- */
  --glow-sm: 0 0 12px -2px #7fd0d859;
  --glow-md: 0 0 28px -4px #7fd0d866, 0 0 6px #7fd0d840;
  --glow-lg: 0 0 64px -8px #7fd0d873, 0 0 18px #7fd0d84d;

  /* ---- Display tracking matches Day: same type, same fit. ---- */
  --tracking-display: -0.045em;
}

/* ===== tokens/night/elevation.css ===== */
/* Ahimsa Night — elevation.
   A drop shadow is invisible on a dark ground, so the same token names do
   a different job: an inset hairline ring establishes the edge, an inner
   top highlight suggests a lit surface, and ambient darkness beneath does
   the lifting. Emphasis comes from --glow-*, never from a brighter fill. */

.ahimsa-night {
  --shadow-xs:    inset 0 0 0 1px #d3dfe914;
  --shadow-sm:    inset 0 0 0 1px #d3dfe91f, 0 1px 0 #ffffff0a;
  --shadow-md:    inset 0 0 0 1px #d3dfe929, inset 0 1px 0 #ffffff14, 0 8px 32px -12px #000000a6;
  --shadow-lg:    inset 0 0 0 1px #d3dfe933, 0 16px 48px -16px #000000b8;
  --shadow-xl:    0 40px 80px -24px #000000cc, 0 0 0 1px #d3dfe91f;
  --shadow-sheet: 0 -16px 48px #000000a6, inset 0 1px 0 #ffffff14;
}

/* ===== tokens/night/type.css ===== */
/* Ahimsa Night — typography.
   The same type as Day, deliberately. Fraunces carries emotional weight
   (display type, titles, italic pull quotes) and DM Sans handles utility
   (labels, small caps, buttons, dates). Night used to speak in an
   ultralight sans and mono small caps; it no longer does, because a
   person who moves between Day and Night has not moved between two
   products, and the voice should not change with the light.

   The split IS the tone. An emotional line goes in italics; a factual line
   stays upright. Never put quotation marks on a pull quote — the italic
   styling is already doing that job.

   One Night-only note: Fraunces thins at small sizes on a dark ground, so
   body copy stays at weight 400 and the 300-weight caption is the lightest
   thing in the system. */

.ahimsa-night {
  --font-display: "Fraunces", Georgia, "Iowan Old Style", serif;
  --font-body:    "DM Sans", -apple-system, system-ui, sans-serif;
  --font-mono:    ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}

.ahimsa-night .ah-display-xl { font-family: var(--font-display); font-size: var(--size-5xl);   font-weight: 400; line-height: 1.0;  letter-spacing: -0.015em; }
.ahimsa-night .ah-display-l  { font-family: var(--font-display); font-size: var(--size-4xl);   font-weight: 400; line-height: 1.0;  letter-spacing: -0.015em; }
.ahimsa-night .ah-display-m  { font-family: var(--font-display); font-size: var(--size-3xl);   font-weight: 400; line-height: 1.05; letter-spacing: -0.01em; }
.ahimsa-night .ah-heading-l  { font-family: var(--font-display); font-size: var(--size-2xl);   font-weight: 400; line-height: 1.1;  letter-spacing: -0.01em; }
.ahimsa-night .ah-heading-m  { font-family: var(--font-display); font-size: var(--size-xl);    font-weight: 400; line-height: 1.15; }
.ahimsa-night .ah-title-l    { font-family: var(--font-display); font-size: var(--size-lg);    font-weight: 400; line-height: 1.2; }
.ahimsa-night .ah-title-m    { font-family: var(--font-display); font-size: var(--size-md);    font-weight: 400; line-height: 1.2; }
.ahimsa-night .ah-body       { font-family: var(--font-body);    font-size: var(--size-base);  font-weight: 400; line-height: 1.4; }
.ahimsa-night .ah-body-serif { font-family: var(--font-display); font-size: var(--size-md);    font-weight: 400; line-height: 1.55; }
.ahimsa-night .ah-caption    { font-family: var(--font-display); font-size: var(--size-sm);    font-weight: 300; line-height: 1.4;  font-style: italic; }
.ahimsa-night .ah-pull-quote { font-family: var(--font-display); font-size: var(--size-lg);    font-weight: 400; line-height: 1.35; font-style: italic; }
.ahimsa-night .ah-label      { font-family: var(--font-body);    font-size: var(--size-sm);    font-weight: 500; line-height: 1.2; }
.ahimsa-night .ah-small-caps { font-family: var(--font-body);    font-size: var(--size-xs);    font-weight: 500; line-height: 1.2;  letter-spacing: 0.22em; text-transform: uppercase; }
.ahimsa-night .ah-micro-caps { font-family: var(--font-body);    font-size: var(--size-micro); font-weight: 500; line-height: 1.2;  letter-spacing: 0.32em; text-transform: uppercase; }

/* The editorial moment: display type that fills a viewport, pulled tight.
   Day's display tracking is negative — the letters lean into each other. */
.ahimsa-night .ah-display-fluid {
  font-family: var(--font-display);
  font-size: var(--size-fluid-display);
  font-weight: 400;
  line-height: 0.92;
  letter-spacing: var(--tracking-display);
  text-wrap: balance;
}

/* Running prose. Mandatory for anything anyone actually reads: --size-base
   is a label size and always was. */
.ahimsa-night .ah-prose {
  font-family: var(--font-display);
  font-size: var(--size-reading);
  font-weight: 400;
  line-height: var(--leading-relaxed);
  text-wrap: pretty;
}

/* ===== tokens/night/surfaces.css ===== */
/* Ahimsa Night — gradient surfaces. GENERATED by tools/build-surfaces.mjs.
   Do not edit. Change the gradients or the generator instead.

   Each class sets a gradient background AND the ink that is measured to
   work on it. Put one on any element and its contents read correctly:

     <section class="ah-surface--dawn"> ... </section>

   Scoped to the theme, because a Day gradient and a Night gradient need
   different ink and two of them once shared a name. To show a Day surface
   inside a Night page (a specimen, say), wrap it: <div class="ahimsa-day">.

   ONE GRADIENT OWNS A SCREEN. Two of these side by side is the rule this
   system breaks least gladly. */

/* deep — worst stop #0f1626; heading 15.34:1, body/secondary 4.51:1, muted 3.02:1 */
.ahimsa-night .ah-surface--deep {
  background: var(--gradient-deep);
  --text-heading:   #e4eef5;
  --text-body:      #e4eef57d;
  --text-secondary: #e4eef57d;
  --text-muted:     #e4eef55e;
  --border-control: #e4eef578;
}

/* midnight — worst stop #182648; heading 12.66:1, body/secondary 4.52:1, muted 3.01:1 */
.ahimsa-night .ah-surface--midnight {
  background: var(--gradient-midnight);
  --text-heading:   #e4eef5;
  --text-body:      #e4eef585;
  --text-secondary: #e4eef585;
  --text-muted:     #e4eef55f;
  --border-control: #e4eef579;
}

/* horizon — worst stop #45717e; heading 4.56:1, body/secondary 4.51:1, muted 3.00:1 */
.ahimsa-night .ah-surface--horizon {
  background: var(--gradient-horizon);
  --text-heading:   #e4eef5;
  --text-body:      #e4eef5fd;
  --text-secondary: #e4eef5fd;
  --text-muted:     #e4eef5ac;
  --border-control: #e4eef5c6;
}

/* aurora — worst stop #45717e; heading 4.56:1, body/secondary 4.51:1, muted 3.00:1 */
.ahimsa-night .ah-surface--aurora {
  background: var(--gradient-aurora);
  --text-heading:   #e4eef5;
  --text-body:      #e4eef5fd;
  --text-secondary: #e4eef5fd;
  --text-muted:     #e4eef5ac;
  --border-control: #e4eef5c6;
}

/* iris — worst stop #555d8d; heading 5.35:1, body/secondary 4.51:1, muted 3.01:1 */
.ahimsa-night .ah-surface--iris {
  background: var(--gradient-iris);
  --text-heading:   #e4eef5;
  --text-body:      #e4eef5de;
  --text-secondary: #e4eef5de;
  --text-muted:     #e4eef598;
  --border-control: #e4eef5b2;
}

/* nightfall — worst stop #57657a; heading 5.03:1, body/secondary 4.51:1, muted 3.02:1 */
.ahimsa-night .ah-surface--nightfall {
  background: var(--gradient-nightfall);
  --text-heading:   #e4eef5;
  --text-body:      #e4eef5e9;
  --text-secondary: #e4eef5e9;
  --text-muted:     #e4eef59f;
  --border-control: #e4eef5b9;
}

/* ember — worst stop #775955; heading 5.34:1, body/secondary 4.52:1, muted 3.01:1 */
.ahimsa-night .ah-surface--ember {
  background: var(--gradient-ember);
  --text-heading:   #e4eef5;
  --text-body:      #e4eef5df;
  --text-secondary: #e4eef5df;
  --text-muted:     #e4eef598;
  --border-control: #e4eef5b2;
}

/* void — worst stop #182648; heading 12.66:1, body/secondary 4.52:1, muted 3.01:1 */
.ahimsa-night .ah-surface--void {
  background: var(--gradient-void);
  --text-heading:   #e4eef5;
  --text-body:      #e4eef585;
  --text-secondary: #e4eef585;
  --text-muted:     #e4eef55f;
  --border-control: #e4eef579;
}

/* ===== base.css ===== */
/* Ahimsa — base.
   The smallest possible reset, plus the accessibility floor that every
   component inherits. Nothing here is decorative. */

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

html {
  /* Never set a px font-size on :root. The whole type scale is in rem
     precisely so that a person's own browser setting still works. */
  -webkit-text-size-adjust: 100%;
}

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--surface-canvas-flat);
}

body {
  font-family: var(--font-body);
  font-size: var(--size-reading);
  color: var(--text-body);
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
}

button, input, textarea, select { font: inherit; color: inherit; }
strong { font-weight: 600; }

/* Two quick taps on the same spot is a zoom gesture on iOS, and cycling a
   small control is exactly two quick taps on the same small target. Once
   the page is zoomed, an app pinned with position: fixed cannot be scrolled
   at all, which reads as scrolling being broken. Controls opt out of that
   one gesture. PINCH TO ZOOM IS NEVER DISABLED — do not add
   maximum-scale or user-scalable=no to your viewport meta. */
button, [role="button"], [role="tab"], [role="checkbox"] { touch-action: manipulation; }

/* ---- Focus. Visible, always, on every focusable thing.
   The system this was extracted from had no :focus-visible rule anywhere
   and blanket-reset outlines on inputs, which left keyboard users with
   nothing at all. Do not reintroduce `outline: none` without putting
   something equally visible in its place. ---- */
:where(a, button, input, textarea, select, summary, [tabindex]):focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: inherit;
}
/* Mouse and touch users don't get a ring; keyboard users always do. */
:where(a, button, input, textarea, select, summary, [tabindex]):focus:not(:focus-visible) {
  outline: none;
}

/* ---- A 44x44 hit area without moving a single painted pixel.
   Small pills and header controls are under the target floor as drawn;
   this grows what the finger hits, not what the eye sees. ---- */
.ah-hit { position: relative; }
.ah-hit::after {
  content: '';
  position: absolute;
  left: 0; right: 0; top: 50%;
  transform: translateY(-50%);
  min-height: 44px;
  height: 100%;
}

/* For a label that must exist for a screen reader and not on screen. */
.ah-visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ---- Motion. Nothing in this system is load-bearing on animation, so a
   reduced-motion preference removes it outright rather than shortening it.
   Durations are NOT lowered: reduced motion must not mean slower or less
   legible, only stiller. ---- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---- Transparency. Five simultaneous blur layers is a rendering cost and
   an accessibility problem; this drops the effect and keeps the surface. ---- */
@media (prefers-reduced-transparency: reduce) {
  .ah-btn--glass, .ah-pill--glass, .ah-chip, .ah-card--glass,
  .ah-mono--glass, .ah-toast > *, .ah-scrim {
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
  }
}

/* ---- Forced colors (Windows High Contrast and friends).
   This system is built on glass and gradients, and forced-colors discards
   both. Rather than fight it, hand the whole surface over to the user's
   own palette: flat system colors, real borders where a shadow used to
   carry the edge. ---- */
@media (forced-colors: active) {
  .ah-card, .ah-sheet, .ah-pill, .ah-chip, .ah-btn, .ah-mono, .ah-toast > * {
    background: Canvas !important;
    color: CanvasText !important;
    border: 1px solid CanvasText !important;
    box-shadow: none !important;
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
  }
  .ah-btn { forced-color-adjust: none; background: ButtonFace !important; color: ButtonText !important; border-color: ButtonText !important; }
  .ah-btn:disabled { color: GrayText !important; border-color: GrayText !important; }
  .ah-pill.is-active, .ah-chip.is-selected { background: Highlight !important; color: HighlightText !important; }
  :focus-visible { outline: 2px solid Highlight !important; }
  /* Ambient decoration has nothing to say in a forced palette. */
  .ah-ambient { display: none !important; }
}

/* ===== components/actions.css ===== */
/* Ahimsa components — actions.
   Button, Pill, Link.

   The governing rule for every control here: LEAVING CARRIES THE SAME
   WEIGHT AS CONTINUING. An escape hatch gets a real, equal-metric control,
   never gray text beside a filled pill — gray text reads as a caption, and
   a caption is not a choice. */

/* ================================================================
   Button — the quiet CTA. Wide-tracked uppercase small caps, fully
   rounded, and it never shouts. Press gently shrinks; it never bounces.
   ================================================================ */
.ah-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  border: none;
  border-radius: var(--radius-full);
  cursor: pointer;
  font-family: var(--font-body);
  font-weight: 500;
  text-transform: uppercase;
  line-height: 1.2;
  padding: 14px var(--space-6);
  font-size: var(--size-xs);
  letter-spacing: 0.22em;
  /* The primary control is the brand accent: Verdigris in Day, Glacier
     Lake in Night. The label is --text-inverse, measured against it. */
  background: var(--accent-primary);
  color: var(--text-inverse);
  box-shadow: var(--shadow-sm);
  transition: opacity var(--duration-fast) var(--ease-standard),
              transform var(--duration-fast) var(--ease-standard);
  user-select: none;
  -webkit-user-select: none;
}
/* Press feedback settles, it does not overshoot. And it is :active rather
   than a pointerdown handler, so sliding off the control still cancels the
   action (WCAG 2.5.2). */
.ah-btn:active { transform: scale(0.97); }

.ah-btn--sm { padding: var(--space-2) var(--space-4); font-size: var(--size-micro); }

/* The escape hatch. An OUTLINE, deliberately — see the note at the top. */
.ah-btn--ghost {
  background: transparent;
  color: var(--text-body);
  /* --border-control, not --border-strong: this line is the control's
     edge, so it owes 3:1 non-text contrast. */
  box-shadow: inset 0 0 0 1px var(--border-control);
}

.ah-btn--glass {
  background: var(--glass-overlay);
  color: var(--text-heading);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  box-shadow: none;
}

/* Destructive. Warm earth in Day, warm copper in Night. Never red:
   removing something is not an emergency, and the color should not
   suggest the person has done something wrong. */
.ah-btn--danger {
  background: var(--accent-warning-surface);
  color: var(--text-inverse);
  box-shadow: none;
}

.ah-btn--wide { width: 100%; }

.ah-btn:disabled, .ah-btn.is-disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* Night lights the primary control rather than lifting it. */
.ahimsa-night .ah-btn { box-shadow: var(--shadow-sm), var(--glow-sm); }
.ahimsa-night .ah-btn--ghost,
.ahimsa-night .ah-btn--glass,
.ahimsa-night .ah-btn--danger { box-shadow: inset 0 0 0 1px var(--border-control); }

/* ================================================================
   Pill — the workhorse. Tags, switchers, filters, quiet actions.
   ================================================================ */
.ah-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px var(--space-3);
  border: none;
  border-radius: var(--radius-full);
  font-family: var(--font-body);
  font-size: var(--size-xs);
  font-weight: 500;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  line-height: 1.2;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  background: var(--surface-elevated);
  color: var(--text-body);
  box-shadow: var(--shadow-xs);
  white-space: nowrap;
  transition: background var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.ah-pill.is-active { background: var(--text-heading); color: var(--text-inverse); }
.ah-pill--outline {
  background: transparent;
  color: var(--text-body);
  box-shadow: inset 0 0 0 1px var(--border-control);
}
.ah-pill--outline.is-active { background: var(--text-heading); color: var(--text-inverse); box-shadow: none; }
.ah-pill--glass {
  background: var(--glass-overlay);
  color: var(--text-heading);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  box-shadow: none;
}
.ah-pill--glass.is-active { background: var(--text-heading); color: var(--text-inverse); }
/* Not a control: no pointer, and it must not be a <button>. */
.ah-pill--static { cursor: default; }

/* A status dot. It never stands alone — the pill's own text always says the
   same thing, because color is not a channel some people have
   (WCAG 1.4.1). */
.ah-pill__dot {
  width: 6px;
  height: 6px;
  border-radius: var(--radius-full);
  flex-shrink: 0;
  background: var(--accent-primary);
}

/* ================================================================
   Link — the growing underline. Quiet at rest, and the rule draws itself
   in on hover and focus rather than the text changing color and jumping.
   ================================================================ */
.ah-link {
  /* Links are the interactive accent (Champaran in Day, Nila in Night);
     --quiet links inherit, for navigation. */
  color: var(--accent-secondary);
  text-decoration: none;
  position: relative;
  display: inline;
  padding-bottom: var(--underline-gap);
}
.ah-link::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: var(--underline-thickness);
  background: currentColor;
  opacity: 0.32;
  transform: scaleX(1);
  transform-origin: left center;
  transition: opacity var(--underline-duration) var(--underline-ease),
              transform var(--underline-duration) var(--underline-ease);
}
.ah-link:hover::after,
.ah-link:focus-visible::after { opacity: 1; }

/* A link whose rule is absent until asked for. Use inside running prose
   where a permanent underline on every reference would be noise. */
.ah-link--quiet { color: inherit; }
.ah-link--quiet::after { transform: scaleX(0); opacity: 1; }
.ah-link--quiet:hover::after,
.ah-link--quiet:focus-visible::after { transform: scaleX(1); }

/* ---- An equal-weight pair of choices. The metrics are identical on
   purpose: this container is the shape of the principle. ---- */
.ah-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.ah-actions--stack { flex-direction: column; align-items: stretch; }

/* ===== components/forms.css ===== */
/* Ahimsa components — forms.
   Field, ToggleChip.

   Inputs have no box. A single bottom rule that strengthens on focus does
   the whole job: a full border around a text field is a container, and a
   container implies something being collected. */

/* ================================================================
   Field
   ================================================================ */
.ah-field { display: block; width: 100%; }

.ah-field__label {
  display: block;
  font-family: var(--font-body);
  font-size: var(--size-xs);
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--text-secondary); /* small text owes 4.5:1 — see .ah-caps */
  margin-bottom: 6px;
}

.ah-field input,
.ah-field textarea {
  width: 100%;
  background: transparent;
  border: none;
  padding: var(--space-2) 0;
  resize: vertical;
  line-height: 1.4;
  border-bottom: 1px solid var(--border-medium);
  transition: border-color var(--duration-base) var(--ease-standard);
  font-family: var(--font-body);
  /* 16px, not the 13px this was extracted at. Safari on iOS zooms the
     viewport when a field under 16px takes focus, and a zoomed
     position: fixed app cannot be scrolled — which reads as the app
     breaking the moment you try to type. */
  font-size: var(--size-reading);
  font-weight: 400;
  color: var(--text-body);
  border-radius: 0;
}
.ah-field input:focus,
.ah-field textarea:focus {
  border-bottom-color: var(--border-strong);
}
/* The bottom rule is reinforcement, not the focus indicator: a keyboard
   user gets the real ring from base.css as well. A 25%-to-35% alpha shift
   is not a visible focus state on its own. */
.ah-field input:focus-visible,
.ah-field textarea:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring);
  outline-offset: 3px;
}

/* The large, reflective variant — for the one answer that matters on a
   screen. Italic serif in both aesthetics. */
.ah-field--essence input,
.ah-field--essence textarea {
  font-family: var(--font-display);
  font-style: italic;
  font-size: var(--size-lg);
  color: var(--text-heading);
}
.ahimsa-night .ah-field--essence input,
.ahimsa-night .ah-field--essence textarea {
  font-style: normal;
  font-weight: 300;
  letter-spacing: 0.02em;
}

/* --text-secondary, not --text-faint: a placeholder is text, and text
   owes 4.5:1. The visible label above it is what actually carries the
   field's meaning, but an unreadable hint is still an unreadable hint. */
.ah-field input::placeholder,
.ah-field textarea::placeholder {
  color: var(--text-secondary);
  font-style: italic;
  font-family: var(--font-display);
}
.ahimsa-night .ah-field input::placeholder,
.ahimsa-night .ah-field textarea::placeholder {
  font-style: normal;
  font-family: var(--font-body);
}

.ah-field__help {
  display: block;
  margin-top: 6px;
  color: var(--text-secondary);
  font-size: var(--size-base);
}

/* ================================================================
   ToggleChip — multi-select, in the emotional voice. Fills dark when
   chosen. Nothing is pre-selected and nothing is required.
   ================================================================ */
.ah-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: var(--space-2) var(--space-4);
  border: none;
  border-radius: var(--radius-full);
  font-family: var(--font-display);
  font-style: italic;
  font-size: var(--size-md);
  font-weight: 400;
  line-height: 1.2;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  background: var(--glass-overlay);
  color: var(--text-heading);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  box-shadow: inset 0 0 0 1px var(--border-control);
  transition: background var(--duration-base) var(--ease-standard),
              color var(--duration-base) var(--ease-standard);
}
.ah-chip.is-selected {
  background: var(--text-heading);
  color: var(--text-inverse);
  box-shadow: none;
}
.ah-chip__dot { width: 7px; height: 7px; border-radius: var(--radius-full); flex-shrink: 0; }

.ahimsa-night .ah-chip {
  font-style: normal;
  font-weight: 300;
  letter-spacing: 0.02em;
  box-shadow: inset 0 0 0 1px var(--border-control);
}
.ahimsa-night .ah-chip.is-selected { box-shadow: var(--glow-sm); }

/* ===== components/surfaces.css ===== */
/* Ahimsa components — surfaces.
   Card, Sheet.

   Three surface levels plus one glass level, expressed as background FIRST
   and shadow second. Sunk surfaces get no shadow at all; the color is the
   elevation. No colored-left-border cards, no hard outlines. */

/* ================================================================
   Card
   ================================================================ */
.ah-card {
  position: relative;
  border-radius: var(--radius-xl);
  padding: var(--space-6);
  overflow: hidden;
  background: var(--surface-elevated);
  color: var(--text-body);
  box-shadow: var(--shadow-md);
  flex-shrink: 0;
}
.ah-card--list { box-shadow: var(--shadow-sm); }
.ah-card--sunk { background: var(--surface-sunk); box-shadow: none; }
.ah-card--glass {
  background: var(--glass-overlay);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: none;
}
/* A gradient card is the one featured moment on a screen. Set the gradient
   inline or with your own class: --gradient-dawn, --gradient-aurora, etc.
   Two gradient cards side by side is the rule this system breaks least
   gladly. */
.ah-card--gradient {
  background: var(--gradient-dawn);
  color: var(--text-heading);
  box-shadow: var(--shadow-lg);
}
.ah-card--flat { box-shadow: none; }
.ah-card--p0 { padding: 0; }

.ah-card.is-tappable { cursor: pointer; }
/* Gentler than a button: a whole card moving 3% would read as a lurch. */
.ah-card.is-tappable:active { transform: scale(0.99); }

.ahimsa-night .ah-card--gradient { box-shadow: var(--shadow-lg), var(--glow-md); }

/* ================================================================
   Sheet — the bottom sheet, and the only modal this system has.

   THREE WAYS OUT, ALWAYS: the scrim, the grab handle, and a visible Close.
   Never a trap. The Close is a real surface at equal weight, not a ghost —
   leaving is as legitimate as staying.
   ================================================================ */
.ah-scrim {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  background: var(--glass-scrim);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  z-index: 50;
}

.ah-sheet {
  width: 100%;
  max-height: 88%;
  overflow-y: auto;
  background: var(--surface-elevated);
  border-top-left-radius: var(--radius-3xl);
  border-top-right-radius: var(--radius-3xl);
  box-shadow: var(--shadow-sheet);
  padding: var(--space-3) var(--space-6) calc(var(--space-7) + var(--safe-bottom, 0px));
  animation: ah-sheet-up var(--duration-slower) var(--ease-decelerate);
}

.ah-sheet__grab {
  width: 40px;
  height: 4px;
  border-radius: var(--radius-full);
  background: var(--border-strong);
  margin: 0 auto var(--space-5);
}

.ah-sheet__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-4);
  gap: var(--space-3);
}
.ah-sheet__title {
  font-family: var(--font-display);
  font-size: var(--size-xl);
  color: var(--text-heading);
  line-height: 1.15;
  margin: 0;
}
.ah-sheet__close {
  border: none;
  background: var(--surface-sunk);
  color: var(--text-secondary);
  width: 44px;
  height: 44px;
  border-radius: var(--radius-full);
  cursor: pointer;
  font-size: var(--size-lg);
  line-height: 1;
  flex-shrink: 0;
}

@keyframes ah-sheet-up {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}

.ahimsa-night .ah-sheet { border-top: 1px solid var(--border-medium); }

/* ================================================================
   Sky — Day's atmosphere, the way .ah-ambient is Night's.
   A light gray sky, a touch deeper overhead, with three large, soft white
   clouds that never move. Put it on the page, a screen, or a hero region;
   cards and text sit on top as they would on flat paper.
   Decorative: it carries no meaning, and under forced-colors it falls
   away to the system canvas.
   ================================================================ */
.ahimsa-day .ah-sky {
  background-color: var(--surface-canvas-flat);
  background-image: var(--ambient-clouds), var(--gradient-sky);
  background-size: 100% 100%, auto;
  background-repeat: no-repeat;
}
@media (forced-colors: active) {
  .ahimsa-day .ah-sky { background-image: none; }
}

/* ===== components/display.css ===== */
/* Ahimsa components — display.
   SmallCapsLabel, PullQuote, Monogram, ProgressDots, StatusDot, SectionRule.

   This system is deliberately icon-light, and its most distinctive "icon"
   is the absence of one: THERE IS NO NOTIFICATION BADGE COMPONENT, by
   design. Updates surface through the rhythm of use, never a red dot. */

/* ================================================================
   SmallCapsLabel — the utility voice. Wide-tracked uppercase, optionally
   led by a short rule, and it reads like a magazine caption.
   ================================================================ */
.ah-caps {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-body);
  font-size: var(--size-xs);
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  line-height: 1.2;
  /* --text-secondary, not --text-muted. This is 10px uppercase, which is
     SMALL text and owes 4.5:1 — the quieter token measures 3.35:1 and
     fails. Nothing in this system sets small text in --text-muted. */
  color: var(--text-secondary);
}
.ah-caps--micro { font-size: var(--size-micro); letter-spacing: 0.32em; }
.ah-caps--rule::before {
  content: '';
  width: 24px;
  height: 1px;
  background: currentColor;
  opacity: 0.6;
  flex-shrink: 0;
}

/* ================================================================
   PullQuote — the emotional voice, and the system's signature.
   NEVER quotation marks: the setting is already the quotation, and the
   marks would only shout.
   ================================================================ */
.ah-quote {
  margin: 0;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: var(--size-lg);
  line-height: 1.35;
  color: var(--text-heading);
  text-wrap: pretty;
  text-align: center;
}
.ah-quote--md { font-size: var(--size-md); }
.ah-quote--xl { font-size: var(--size-xl); }
.ah-quote--bordered {
  text-align: left;
  border-left: 1px solid var(--border-medium);
  padding-left: var(--space-4);
}
.ahimsa-night .ah-quote {
  font-style: normal;
  font-weight: 200;
  letter-spacing: 0.03em;
  line-height: 1.45;
  color: var(--frost-300);
}

/* ================================================================
   Monogram — a circular avatar that is an initial, not a photograph.
   Set --ah-mono-size (default 56px); the letter scales with the box.
   ================================================================ */
.ah-mono {
  --ah-mono-size: 56px;
  width: var(--ah-mono-size);
  height: var(--ah-mono-size);
  font-size: calc(var(--ah-mono-size) * 0.62);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-full);
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  line-height: 1;
  flex-shrink: 0;
  background: var(--surface-elevated);
  color: var(--text-heading);
  box-shadow: var(--shadow-sm);
}
.ah-mono--sunk { background: var(--surface-sunk); color: var(--text-secondary); box-shadow: none; }
.ah-mono--glass {
  background: var(--glass-overlay);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  box-shadow: inset 0 0 0 1px var(--glass-overlay-strong);
}
.ah-mono--s20  { --ah-mono-size: 20px; }
.ah-mono--s44  { --ah-mono-size: 44px; }
.ah-mono--s84  { --ah-mono-size: 84px; }
.ah-mono--s110 { --ah-mono-size: 110px; }
.ah-mono--s144 { --ah-mono-size: 144px; }
/* In Night the monogram is ringed with a hairline and lit rather than
   lifted. The letter itself is the same as Day's. */
.ahimsa-night .ah-mono {
  box-shadow: inset 0 0 0 1px var(--border-control), var(--glow-sm);
}
.ahimsa-night .ah-mono--sunk { box-shadow: inset 0 0 0 1px var(--border-medium); }

/* ================================================================
   ProgressDots — where you are, with no count and no urgency.
   Each dot is a real button: pagination you can only reach by dragging
   is not reachable (WCAG 2.5.7).
   ================================================================ */
.ah-dots { display: inline-flex; align-items: center; gap: 2px; }
/* The control is a real 44x44 box; the dot is painted inside it. An
   invisible ::after hit area works for a finger but is invisible to an
   audit, and "it is accessible, trust me" is not a position this system
   takes. */
.ah-dots button,
.ah-dots span {
  width: 44px;
  height: 44px;
  padding: 0;
  border: none;
  background: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.ah-dots button { cursor: pointer; }
.ah-dots button::before,
.ah-dots span::before {
  content: '';
  display: block;
  width: 5px;
  height: 5px;
  border-radius: var(--radius-full);
  background: var(--border-strong);
  opacity: 0.5;
  transition: width var(--duration-fast) var(--ease-standard),
              opacity var(--duration-fast) var(--ease-standard);
}
.ah-dots .is-on::before { width: 22px; opacity: 1; background: var(--text-heading); }

/* ================================================================
   StatusDot — never the only carrier of its meaning. The adjacent text
   says the same thing (WCAG 1.4.1).
   ================================================================ */
.ah-sdot {
  width: 7px;
  height: 7px;
  border-radius: var(--radius-full);
  flex-shrink: 0;
  display: inline-block;
  background: var(--text-muted);
}

/* ================================================================
   SectionRule — the signature "── label ──────".
   ================================================================ */
.ah-rule-row { display: flex; align-items: center; gap: var(--space-2); }
.ah-rule-row::before {
  content: '';
  width: 16px;
  border-top: 1px solid var(--border-medium);
  flex-shrink: 0;
}
.ah-rule-row::after {
  content: '';
  flex: 1;
  border-top: 1px solid var(--border-medium);
}

/* ===== components/feedback.css ===== */
/* Ahimsa components — feedback.
   Toast, Notice.

   This is the file where the attention principle is load-bearing, so read
   this before adding anything to it:

   NOTHING IN AN AHIMSA INTERFACE APPEARS ON ITS OWN. The test is not "is it
   dismissible" but DID THE PERSON CAUSE THIS, HERE, NOW?

   There is deliberately no UpdateBar, no InstallBar, no banner, no modal
   and no badge in this system. A floating bar that slides in to announce
   something the person did not ask about spends their attention on the
   product's behalf, and whatever they were doing is what they should still
   be doing. Version news belongs in Settings, behind a control they press.
   See patterns/version-check. */

/* ================================================================
   Toast — the acknowledgement of an action just taken. That is its ONLY
   legitimate use. It never carries news, never announces an arrival, and
   never appears on a timer the person did not start.

   Always role="status" with aria-live="polite". NEVER role="alert" or
   aria-live="assertive": an assertive region interrupts a screen-reader
   user mid-sentence, which is the same harm in a different channel.
   ================================================================ */
.ah-toast {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(var(--safe-bottom, 0px) + var(--space-6));
  z-index: 60;
  display: flex;
  justify-content: center;
  /* It cannot be in the way of anything, because it was not invited to be. */
  pointer-events: none;
  padding: 0 var(--space-5);
}
.ah-toast > * {
  background: var(--glass-overlay-strong);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-radius: var(--radius-full);
  padding: var(--space-2) 18px;
  box-shadow: var(--shadow-md);
  max-width: 320px;
  color: var(--text-heading);
  animation: ah-rise var(--duration-base) var(--ease-standard);
}
.ahimsa-night .ah-toast > * {
  box-shadow: var(--shadow-md), var(--glow-sm);
}

@keyframes ah-rise {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ================================================================
   Notice — the replacement for every banner this system refuses.
   It sits IN the document flow, where the person already is, and it is
   rendered because they navigated to it or because they just acted. It
   does not float, does not cover anything, and does not take focus.
   ================================================================ */
.ah-notice {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-lg);
  background: var(--surface-sunk);
  color: var(--text-body);
  font-size: var(--size-base);
}
.ah-notice__title {
  font-family: var(--font-body);
  font-size: var(--size-xs);
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--text-secondary); /* small text owes 4.5:1 — see .ah-caps */
}

/* Something did not work. Stated in the same voice as everything else:
   a warm tint and a sentence, not a red box and an exclamation mark.
   The person has not done anything wrong. */
.ah-notice--warning {
  background: color-mix(in srgb, var(--accent-warning) 14%, var(--surface-elevated));
  color: var(--text-body);
}
.ah-notice--warning .ah-notice__title { color: var(--accent-warning); }

.ah-notice__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

/* ===== patterns/shell.css ===== */
/* Ahimsa patterns — shells.
   Two of them, and picking the right one matters.

   .ah-page — a normal document. Fluid gutters, a reading measure, scrolls
     the way the browser intends. Use this for sites, docs, anything read.

   .ah-app — a fixed full-viewport application frame with safe-area insets,
     widening in steps on larger screens so a desktop window gets a
     comfortable app rather than a narrow strip. Use this for installed
     PWAs. It is a deliberate trade: a fixed shell cannot reflow the way a
     document can, so do not reach for it to hold an article. */

/* ================================================================
   Page shell
   ================================================================ */
.ah-page {
  max-width: 1440px;
  margin: 0 auto;
  padding-inline: var(--gutter-fluid);
  background: var(--surface-canvas);
}
/* The reading measure. Long lines are a readability problem long before
   they are an aesthetic one. */
.ah-measure { max-width: 68ch; }
.ah-measure--wide { max-width: 88ch; }

.ah-stack { display: flex; flex-direction: column; gap: var(--space-4); }
.ah-stack--tight { gap: var(--space-2); }
.ah-stack--loose { gap: var(--space-8); }
/* Magazine composition: at least one full unit of air above and below a
   heading. If a section feels crowded, remove elements before shrinking
   type. */
.ah-section { padding-block: var(--space-16); }

/* ================================================================
   App shell
   ================================================================ */
.ah-app {
  position: fixed;
  inset: 0;
  overflow: hidden;
  background: var(--surface-canvas);
  --gutter: 20px;
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
  --safe-right: env(safe-area-inset-right, 0px);
  --top-pad: calc(var(--safe-top) + 14px);
  --scroll-tail: calc(var(--safe-bottom) + var(--space-12));
}
/* Requires <meta name="viewport" content="width=device-width,
   initial-scale=1, viewport-fit=cover"> for env(safe-area-inset-*) to be
   live. Note what is NOT in that meta: no maximum-scale, no
   user-scalable=no. Pinch zoom stays. */

@media (min-width: 560px) {
  .ah-app {
    max-width: 480px;
    left: 50%;
    transform: translateX(-50%);
    box-shadow: var(--shadow-xl);
  }
}
@media (min-width: 900px)  { .ah-app { max-width: 720px; --gutter: 32px; } }
@media (min-width: 1200px) { .ah-app { max-width: 900px; --gutter: 40px; } }

.ah-screen {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  padding-top: var(--top-pad);
  background: var(--surface-canvas);
}
.ah-screen > *:not(.ah-scroll) { flex-shrink: 0; }
.ah-screen--over { z-index: 6; }

.ah-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  /* y only. Setting this on x swallows horizontal trackpad swipes and can
     turn a pager into a one-way trap with no pointer route back. */
  overscroll-behavior-y: contain;
  padding: 6px var(--gutter) 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
/* A spacer BLOCK, not bottom padding. End padding on a flex scroll
   container is not reliably part of what the browser will scroll to; a
   block is content, and every engine scrolls to the end of its content. */
.ah-scroll::after {
  content: '';
  display: block;
  flex: 0 0 auto;
  height: var(--scroll-tail, 0px);
}

/* Header row */
.ah-hdr {
  padding: 6px var(--gutter) var(--space-2);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-shrink: 0;
}

/* A horizontal row of chips that fades at the edge instead of ending
   abruptly, and wraps rather than scrolling once there is room. A
   horizontal scroller is a drag-only interaction, so pair it with the
   same choices reachable another way. */
.ah-hrow {
  display: flex;
  gap: var(--space-2);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  padding-inline: var(--gutter);
  mask-image: linear-gradient(90deg, #000 calc(100% - 18px), transparent);
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 18px), transparent);
}
@media (min-width: 900px) {
  .ah-hrow { flex-wrap: wrap; overflow-x: visible; mask-image: none; -webkit-mask-image: none; }
}

/* A list of self-contained rows: one column on a phone, two when the
   window allows it. */
.ah-list { display: flex; flex-direction: column; gap: var(--space-2); }
@media (min-width: 900px) {
  .ah-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* A sticky footer over a gradient uses strong glass rather than restarting
   the gradient, which would read as a seam. */
.ah-footer {
  flex-shrink: 0;
  padding: var(--space-3) var(--gutter) calc(var(--space-5) + var(--safe-bottom, 0px));
  background: var(--glass-overlay-strong);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  border-top: 1px solid var(--border-subtle);
}

/* Opt-in, and only inside an app shell: hiding scrollbars removes a
   pointer target that some people rely on, so it is never the default. */
.ah-scroll--bare { scrollbar-width: none; }
.ah-scroll--bare::-webkit-scrollbar { display: none; }

/* ===== ambient/clouds.css ===== */
/* Ahimsa Day — the clouds.
   Three large, soft cloud banks, white at 75-85% opacity, blurred until
   no edge survives: haze with a shape, not a drawing of a cloud. They sit
   in a fixed arrangement, so the sky is the same every day, and nothing in
   them moves. Day's counterpart to Night's stars: there to give the
   feeling of daylight and to be ignored. They never carry meaning.

   .ah-sky composes them over --gradient-sky. To use them yourself, put
   var(--ambient-clouds) over --gradient-sky at background-size 100% 100%.

   The image is an SVG on a 390x844 canvas, stretched to the element
   (preserveAspectRatio none, like the stars). Each cloud is the same five
   puffs, placed by this table and blurred by 6% of its width:

     cloud   centre (x, y of the canvas)   width (of canvas width)   opacity
     1       5%, 13%                       105%                      .85
     2       100%, 50%                     100%                      .75
     3       8%, 86%                       110%                      .80

     puffs   (dx, dy, size), in units of the cloud's width; each puff is an
             ellipse 1.5 x size wide and 1 x size tall
             (-.32, .06, .26) (-.10, -.08, .34) (.16, -.04, .30)
             (.36, .08, .22) (0, .12, .40)

   Never pure white: every puff is translucent, as --glass-overlay is. */

:root, .ahimsa-day { --ambient-clouds: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 390 844' preserveAspectRatio='none'%3E%3Cdefs%3E%3Cfilter id='c0' filterUnits='userSpaceOnUse' x='-300' y='-300' width='990' height='1444'%3E%3CfeGaussianBlur stdDeviation='24.6'/%3E%3C/filter%3E%3Cfilter id='c1' filterUnits='userSpaceOnUse' x='-300' y='-300' width='990' height='1444'%3E%3CfeGaussianBlur stdDeviation='23.4'/%3E%3C/filter%3E%3Cfilter id='c2' filterUnits='userSpaceOnUse' x='-300' y='-300' width='990' height='1444'%3E%3CfeGaussianBlur stdDeviation='25.7'/%3E%3C/filter%3E%3C/defs%3E%3Cg fill='%23ffffff'%3E%3Cg filter='url(%23c0)'%3E%3Cellipse cx='-111.5' cy='134.3' rx='79.9' ry='53.2' fill-opacity='0.85'/%3E%3Cellipse cx='-21.5' cy='77' rx='104.4' ry='69.6' fill-opacity='0.85'/%3E%3Cellipse cx='85' cy='93.3' rx='92.1' ry='61.4' fill-opacity='0.85'/%3E%3Cellipse cx='166.9' cy='142.5' rx='67.6' ry='45' fill-opacity='0.85'/%3E%3Cellipse cx='19.5' cy='158.9' rx='122.9' ry='81.9' fill-opacity='0.85'/%3E%3C/g%3E%3Cg filter='url(%23c1)'%3E%3Cellipse cx='265.2' cy='445.4' rx='76' ry='50.7' fill-opacity='0.75'/%3E%3Cellipse cx='351' cy='390.8' rx='99.5' ry='66.3' fill-opacity='0.75'/%3E%3Cellipse cx='452.4' cy='406.4' rx='87.7' ry='58.5' fill-opacity='0.75'/%3E%3Cellipse cx='530.4' cy='453.2' rx='64.4' ry='42.9' fill-opacity='0.75'/%3E%3Cellipse cx='390' cy='468.8' rx='117' ry='78' fill-opacity='0.75'/%3E%3C/g%3E%3Cg filter='url(%23c2)'%3E%3Cellipse cx='-106.1' cy='751.6' rx='83.7' ry='55.8' fill-opacity='0.8'/%3E%3Cellipse cx='-11.7' cy='691.5' rx='109.4' ry='72.9' fill-opacity='0.8'/%3E%3Cellipse cx='99.8' cy='708.7' rx='96.5' ry='64.4' fill-opacity='0.8'/%3E%3Cellipse cx='185.6' cy='760.2' rx='70.8' ry='47.2' fill-opacity='0.8'/%3E%3Cellipse cx='31.2' cy='777.3' rx='128.7' ry='85.8' fill-opacity='0.8'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); }

/* ===== ambient/ambient.css ===== */
/* Ahimsa Night — the ambient layer.
   =====================================================================
   WHAT THIS IS
   A decorative field that sits behind a screen and does nothing else. It
   is the Night theme's answer to Day's clouded sky: atmosphere, not
   information.

   The reference implementations (littleworkshop's TRACK, uberviz's Splice)
   reach this look through three.js with a postprocessing chain — FXAA,
   RGBShift, BarrelBlur, HueSaturation, a scanline pass, Shake, and additive
   glow over procedural geometry. Every one of those has a faithful CSS or
   SVG equivalent, so none of it needs WebGL, a canvas, or a single byte of
   JavaScript:

     additive glow   -> mix-blend-mode: plus-lighter (literally additive)
     SimplexNoise    -> feTurbulence fractalNoise, as a data-URI image
     Lines shader    -> repeating-linear-gradient
     BarrelBlur      -> radial-gradient falloff
     HueSaturation   -> filter: hue-rotate on the ambient layer only
     RGBShift        -> per-channel feOffset (see aberration.svg)
     geometry        -> repeating gradients and SVG line motifs

   WHAT IT MUST NEVER DO
   This layer is where a futuristic register usually starts doing harm, so
   the constraints are hard, not stylistic:

     - It is aria-hidden and purely decorative. It NEVER encodes state,
       progress, or anything a person could miss by ignoring it.
     - It never reacts to error. A glitch effect that fires when something
       goes wrong tells someone their screen is broken.
     - It never flashes. Nothing here pulses faster than 0.1Hz, and no
       animation is shorter than 2s — well outside WCAG 2.3.1's three-flash
       threshold.
     - It animates opacity and hue only. Never transform, never position:
       movement in peripheral vision is a demand for attention.
     - It disappears entirely under prefers-reduced-motion,
       prefers-reduced-transparency, and forced-colors.

   Spectacle that asks for nothing is the only kind this system allows.
   ===================================================================== */

.ahimsa-night {
  /* Tuning knobs. These are the four values worth changing; everything
     else in this file is derived from them. */
  --ambient-grain:    0.05;
  --ambient-scan:     0.035;
  --ambient-bloom:    0.5;
  --ambient-lines:    0.1;
  --ambient-vignette: 0.75;
  --ambient-drift:    32s;
}

.ah-ambient {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  /* Decorative. Always paired with aria-hidden="true" in the markup. */
}

/* Content sits above the field. */
.ah-ambient ~ * { position: relative; z-index: 1; }

/* ---- The ground. One radial, lit faintly from above, falling away to
   almost nothing at the edges. ---- */
.ah-ambient--void { background: var(--gradient-void); }

/* ---- Bloom. Additive light, stacked. plus-lighter is real additive
   blending, which is exactly what the reference does in a shader. ---- */
.ah-ambient--bloom::before {
  content: '';
  position: absolute;
  inset: -20%;
  mix-blend-mode: plus-lighter;
  opacity: var(--ambient-bloom);
  background:
    radial-gradient(38% 28% at 22% 18%, #49788540 0%, transparent 70%),
    radial-gradient(30% 24% at 78% 32%, #5b639533 0%, transparent 72%),
    radial-gradient(46% 34% at 50% 96%, #74b8c826 0%, transparent 76%);
  animation: ah-ambient-breathe var(--ambient-drift) var(--ease-standard) infinite alternate;
}

/* ---- Grain. feTurbulence, desaturated, as a data-URI image. Soft-light
   keeps it from lifting the black point. ---- */
.ah-ambient--grain::after {
  content: '';
  position: absolute;
  inset: 0;
  mix-blend-mode: soft-light;
  opacity: var(--ambient-grain);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 180px 180px;
}

/* ---- Scanlines. The Lines shader, at a 3px pitch and a whisper of
   opacity. Any stronger and it becomes a texture, which this system
   does not do. ---- */
.ah-ambient--scan {
  background-image: repeating-linear-gradient(
    0deg,
    transparent 0 2px,
    #d3dfe9 2px 3px
  );
  background-blend-mode: overlay;
  opacity: 1;
}
.ah-ambient--scan::before {
  content: '';
  position: absolute;
  inset: 0;
  mix-blend-mode: overlay;
  opacity: var(--ambient-scan);
  background: repeating-linear-gradient(0deg, transparent 0 2px, #d3dfe9 2px 3px);
}

/* ---- Vignette. BarrelBlur's falloff, without the blur cost. ---- */
.ah-ambient--vignette {
  box-shadow: inset 0 0 min(40vw, 320px) min(8vw, 64px) #05080c;
}

/* ---- Geometry. Procedural in the reference; here, gradients that read
   the same at rest. TRACK's environment is line and plane, and neither
   needs a mesh to suggest it. ---- */

/* A ruled horizon, receding. */
.ah-ambient--horizon::before {
  content: '';
  position: absolute;
  left: -50%; right: -50%; bottom: 0;
  height: 62%;
  opacity: var(--ambient-lines);
  background:
    repeating-linear-gradient(0deg, transparent 0 38px, #7fd0d8 38px 39px),
    repeating-linear-gradient(90deg, transparent 0 68px, #7fd0d8 68px 69px);
  mask-image: linear-gradient(to top, #000 0%, transparent 92%);
  -webkit-mask-image: linear-gradient(to top, #000 0%, transparent 92%);
  transform: perspective(420px) rotateX(74deg);
  transform-origin: bottom center;
}

/* A flat measured grid — the HUD register rather than the landscape one. */
.ah-ambient--grid::before {
  content: '';
  position: absolute;
  inset: 0;
  opacity: calc(var(--ambient-lines) * 0.7);
  background:
    repeating-linear-gradient(0deg, transparent 0 47px, #a0a8f5 47px 48px),
    repeating-linear-gradient(90deg, transparent 0 47px, #a0a8f5 47px 48px);
  mask-image: radial-gradient(70% 60% at 50% 40%, #000 0%, transparent 100%);
  -webkit-mask-image: radial-gradient(70% 60% at 50% 40%, #000 0%, transparent 100%);
}

/* Concentric isolines — a contour map of nothing in particular. */
.ah-ambient--isolines::before {
  content: '';
  position: absolute;
  inset: -10%;
  opacity: var(--ambient-lines);
  background: repeating-radial-gradient(
    closest-side at 50% 42%,
    transparent 0 42px,
    #74b8c8 42px 43px
  );
  mask-image: radial-gradient(60% 60% at 50% 42%, #000 20%, transparent 100%);
  -webkit-mask-image: radial-gradient(60% 60% at 50% 42%, #000 20%, transparent 100%);
  animation: ah-ambient-hue var(--ambient-drift) var(--ease-standard) infinite alternate;
}

/* ---- Stars. A faint, static field: 130 pinpricks of ice-white at 14-56%
   opacity, in a fixed arrangement so the sky is the same every night.
   Nothing twinkles and nothing moves. It is there to give the feeling of
   night and to be ignored; it never carries meaning. Put it over
   --gradient-midnight (or --gradient-void) and under the content. ---- */
:root, .ahimsa-night { --ambient-stars: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100%25' height='100%25'%3E%3Cg fill='%23eaf0ff'%3E%3Ccircle cx='73.1%25' cy='55.5%25' r='0.5' opacity='.29'/%3E%3Ccircle cx='64.9%25' cy='36.0%25' r='0.5' opacity='.41'/%3E%3Ccircle cx='55.3%25' cy='37.8%25' r='0.8' opacity='.18'/%3E%3Ccircle cx='80.4%25' cy='60.8%25' r='0.5' opacity='.42'/%3E%3Ccircle cx='74.7%25' cy='87.1%25' r='0.5' opacity='.39'/%3E%3Ccircle cx='77.9%25' cy='17.8%25' r='0.8' opacity='.44'/%3E%3Ccircle cx='59.6%25' cy='36.0%25' r='0.5' opacity='.53'/%3E%3Ccircle cx='70.2%25' cy='4.3%25' r='0.8' opacity='.26'/%3E%3Ccircle cx='52.1%25' cy='70.6%25' r='0.5' opacity='.34'/%3E%3Ccircle cx='5.7%25' cy='11.6%25' r='0.5' opacity='.55'/%3E%3Ccircle cx='44.8%25' cy='34.2%25' r='0.8' opacity='.31'/%3E%3Ccircle cx='51.4%25' cy='68.6%25' r='0.5' opacity='.35'/%3E%3Ccircle cx='93.5%25' cy='24.6%25' r='0.8' opacity='.15'/%3E%3Ccircle cx='51.4%25' cy='11.4%25' r='0.5' opacity='.38'/%3E%3Ccircle cx='93.5%25' cy='71.8%25' r='0.8' opacity='.26'/%3E%3Ccircle cx='61.1%25' cy='79.1%25' r='0.5' opacity='.35'/%3E%3Ccircle cx='35.7%25' cy='21.6%25' r='1.15' opacity='.28'/%3E%3Ccircle cx='26.1%25' cy='69.6%25' r='0.5' opacity='.22'/%3E%3Ccircle cx='54.9%25' cy='94.3%25' r='0.5' opacity='.53'/%3E%3Ccircle cx='4.7%25' cy='90.6%25' r='0.5' opacity='.36'/%3E%3Ccircle cx='85.8%25' cy='70.2%25' r='0.5' opacity='.22'/%3E%3Ccircle cx='19.3%25' cy='94.5%25' r='0.5' opacity='.39'/%3E%3Ccircle cx='89.3%25' cy='49.6%25' r='0.5' opacity='.35'/%3E%3Ccircle cx='27.5%25' cy='26.0%25' r='0.5' opacity='.38'/%3E%3Ccircle cx='74.8%25' cy='3.7%25' r='0.5' opacity='.16'/%3E%3Ccircle cx='79.1%25' cy='83.8%25' r='0.5' opacity='.38'/%3E%3Ccircle cx='9.3%25' cy='89.2%25' r='0.5' opacity='.54'/%3E%3Ccircle cx='82.2%25' cy='74.6%25' r='0.5' opacity='.44'/%3E%3Ccircle cx='56.4%25' cy='28.1%25' r='0.8' opacity='.28'/%3E%3Ccircle cx='99.4%25' cy='78.5%25' r='0.8' opacity='.4'/%3E%3Ccircle cx='92.1%25' cy='3.4%25' r='0.5' opacity='.18'/%3E%3Ccircle cx='8.7%25' cy='75.6%25' r='0.8' opacity='.44'/%3E%3Ccircle cx='36.7%25' cy='16.8%25' r='0.5' opacity='.18'/%3E%3Ccircle cx='25.4%25' cy='18.3%25' r='0.5' opacity='.15'/%3E%3Ccircle cx='17.9%25' cy='79.8%25' r='0.5' opacity='.44'/%3E%3Ccircle cx='53.4%25' cy='28.2%25' r='0.5' opacity='.18'/%3E%3Ccircle cx='15.9%25' cy='24.2%25' r='0.5' opacity='.28'/%3E%3Ccircle cx='73.2%25' cy='81.5%25' r='0.5' opacity='.55'/%3E%3Ccircle cx='3.2%25' cy='57.6%25' r='0.5' opacity='.55'/%3E%3Ccircle cx='81.4%25' cy='1.9%25' r='1.15' opacity='.21'/%3E%3Ccircle cx='35.3%25' cy='82.1%25' r='0.8' opacity='.5'/%3E%3Ccircle cx='29.9%25' cy='83.0%25' r='0.5' opacity='.55'/%3E%3Ccircle cx='97.9%25' cy='54.5%25' r='0.5' opacity='.19'/%3E%3Ccircle cx='62.9%25' cy='83.9%25' r='0.5' opacity='.54'/%3E%3Ccircle cx='26.7%25' cy='91.8%25' r='0.5' opacity='.16'/%3E%3Ccircle cx='33.1%25' cy='5.7%25' r='0.5' opacity='.53'/%3E%3Ccircle cx='28.2%25' cy='71.5%25' r='0.5' opacity='.35'/%3E%3Ccircle cx='67.5%25' cy='73.0%25' r='0.8' opacity='.42'/%3E%3Ccircle cx='83.7%25' cy='51.8%25' r='0.5' opacity='.31'/%3E%3Ccircle cx='36.3%25' cy='38.6%25' r='0.5' opacity='.43'/%3E%3Ccircle cx='29.7%25' cy='11.7%25' r='0.5' opacity='.15'/%3E%3Ccircle cx='52.1%25' cy='51.0%25' r='0.5' opacity='.2'/%3E%3Ccircle cx='83.2%25' cy='13.1%25' r='0.5' opacity='.14'/%3E%3Ccircle cx='37.2%25' cy='71.5%25' r='0.5' opacity='.28'/%3E%3Ccircle cx='9.3%25' cy='44.9%25' r='0.5' opacity='.37'/%3E%3Ccircle cx='46.0%25' cy='32.7%25' r='0.5' opacity='.23'/%3E%3Ccircle cx='20.8%25' cy='3.3%25' r='0.5' opacity='.39'/%3E%3Ccircle cx='80.3%25' cy='72.6%25' r='0.5' opacity='.21'/%3E%3Ccircle cx='45.2%25' cy='22.1%25' r='0.8' opacity='.16'/%3E%3Ccircle cx='38.4%25' cy='80.1%25' r='0.5' opacity='.33'/%3E%3Ccircle cx='43.8%25' cy='71.3%25' r='0.5' opacity='.48'/%3E%3Ccircle cx='87.3%25' cy='42.3%25' r='0.5' opacity='.54'/%3E%3Ccircle cx='11.5%25' cy='81.7%25' r='0.8' opacity='.26'/%3E%3Ccircle cx='11.3%25' cy='65.8%25' r='0.5' opacity='.23'/%3E%3Ccircle cx='17.2%25' cy='28.1%25' r='1.15' opacity='.21'/%3E%3Ccircle cx='30.6%25' cy='4.8%25' r='0.8' opacity='.21'/%3E%3Ccircle cx='15.1%25' cy='76.8%25' r='0.8' opacity='.53'/%3E%3Ccircle cx='70.2%25' cy='15.0%25' r='0.5' opacity='.36'/%3E%3Ccircle cx='80.3%25' cy='24.3%25' r='0.5' opacity='.53'/%3E%3Ccircle cx='81.2%25' cy='20.1%25' r='0.5' opacity='.39'/%3E%3Ccircle cx='70.6%25' cy='70.2%25' r='0.5' opacity='.34'/%3E%3Ccircle cx='73.6%25' cy='51.8%25' r='0.5' opacity='.26'/%3E%3Ccircle cx='47.8%25' cy='92.9%25' r='0.8' opacity='.31'/%3E%3Ccircle cx='0.7%25' cy='64.2%25' r='0.5' opacity='.5'/%3E%3Ccircle cx='50.0%25' cy='68.9%25' r='0.5' opacity='.23'/%3E%3Ccircle cx='72.5%25' cy='43.6%25' r='0.5' opacity='.15'/%3E%3Ccircle cx='78.0%25' cy='52.6%25' r='0.5' opacity='.54'/%3E%3Ccircle cx='42.9%25' cy='41.0%25' r='1.15' opacity='.39'/%3E%3Ccircle cx='26.9%25' cy='20.0%25' r='0.5' opacity='.49'/%3E%3Ccircle cx='60.9%25' cy='99.6%25' r='0.5' opacity='.16'/%3E%3Ccircle cx='64.1%25' cy='18.6%25' r='0.5' opacity='.27'/%3E%3Ccircle cx='59.6%25' cy='48.5%25' r='0.5' opacity='.4'/%3E%3Ccircle cx='27.4%25' cy='46.5%25' r='0.5' opacity='.28'/%3E%3Ccircle cx='85.1%25' cy='82.2%25' r='0.8' opacity='.34'/%3E%3Ccircle cx='30.5%25' cy='23.3%25' r='0.8' opacity='.34'/%3E%3Ccircle cx='60.7%25' cy='72.3%25' r='0.5' opacity='.42'/%3E%3Ccircle cx='39.6%25' cy='25.1%25' r='0.5' opacity='.27'/%3E%3Ccircle cx='80.2%25' cy='78.4%25' r='0.8' opacity='.36'/%3E%3Ccircle cx='56.0%25' cy='87.5%25' r='0.5' opacity='.5'/%3E%3Ccircle cx='90.4%25' cy='50.6%25' r='0.8' opacity='.54'/%3E%3Ccircle cx='1.8%25' cy='58.1%25' r='0.5' opacity='.2'/%3E%3Ccircle cx='3.7%25' cy='21.8%25' r='0.8' opacity='.23'/%3E%3Ccircle cx='33.0%25' cy='40.7%25' r='0.8' opacity='.16'/%3E%3Ccircle cx='13.4%25' cy='9.4%25' r='0.5' opacity='.55'/%3E%3Ccircle cx='27.0%25' cy='62.8%25' r='0.8' opacity='.27'/%3E%3Ccircle cx='40.8%25' cy='67.7%25' r='0.8' opacity='.21'/%3E%3Ccircle cx='85.7%25' cy='99.2%25' r='0.5' opacity='.51'/%3E%3Ccircle cx='60.4%25' cy='7.7%25' r='0.5' opacity='.48'/%3E%3Ccircle cx='69.9%25' cy='95.8%25' r='0.5' opacity='.34'/%3E%3Ccircle cx='71.0%25' cy='65.4%25' r='0.5' opacity='.4'/%3E%3Ccircle cx='47.6%25' cy='88.2%25' r='0.8' opacity='.34'/%3E%3Ccircle cx='19.5%25' cy='30.1%25' r='0.8' opacity='.5'/%3E%3Ccircle cx='55.7%25' cy='52.0%25' r='0.5' opacity='.5'/%3E%3Ccircle cx='73.5%25' cy='25.6%25' r='0.5' opacity='.33'/%3E%3Ccircle cx='31.6%25' cy='60.7%25' r='0.5' opacity='.19'/%3E%3Ccircle cx='36.2%25' cy='23.0%25' r='0.8' opacity='.49'/%3E%3Ccircle cx='36.5%25' cy='54.9%25' r='0.5' opacity='.25'/%3E%3Ccircle cx='14.1%25' cy='50.1%25' r='0.5' opacity='.55'/%3E%3Ccircle cx='34.7%25' cy='75.8%25' r='0.5' opacity='.51'/%3E%3Ccircle cx='44.5%25' cy='79.0%25' r='0.8' opacity='.49'/%3E%3Ccircle cx='47.8%25' cy='44.2%25' r='0.5' opacity='.51'/%3E%3Ccircle cx='79.7%25' cy='45.0%25' r='0.5' opacity='.44'/%3E%3Ccircle cx='98.3%25' cy='87.1%25' r='0.5' opacity='.35'/%3E%3Ccircle cx='37.5%25' cy='65.6%25' r='0.8' opacity='.55'/%3E%3Ccircle cx='64.2%25' cy='32.7%25' r='0.5' opacity='.26'/%3E%3Ccircle cx='20.7%25' cy='11.1%25' r='0.5' opacity='.16'/%3E%3Ccircle cx='88.0%25' cy='74.5%25' r='0.8' opacity='.49'/%3E%3Ccircle cx='28.2%25' cy='71.0%25' r='0.8' opacity='.34'/%3E%3Ccircle cx='1.2%25' cy='36.1%25' r='0.5' opacity='.2'/%3E%3Ccircle cx='73.5%25' cy='65.7%25' r='0.5' opacity='.23'/%3E%3Ccircle cx='59.6%25' cy='87.1%25' r='0.8' opacity='.4'/%3E%3Ccircle cx='31.1%25' cy='99.9%25' r='0.5' opacity='.5'/%3E%3Ccircle cx='61.8%25' cy='76.3%25' r='0.5' opacity='.37'/%3E%3Ccircle cx='67.4%25' cy='92.1%25' r='0.5' opacity='.2'/%3E%3Ccircle cx='2.8%25' cy='7.0%25' r='0.5' opacity='.27'/%3E%3Ccircle cx='86.1%25' cy='43.7%25' r='0.5' opacity='.51'/%3E%3Ccircle cx='60.4%25' cy='7.8%25' r='0.5' opacity='.4'/%3E%3Ccircle cx='49.0%25' cy='13.2%25' r='0.8' opacity='.2'/%3E%3Ccircle cx='33.2%25' cy='21.4%25' r='0.5' opacity='.22'/%3E%3Ccircle cx='1.8%25' cy='61.7%25' r='0.5' opacity='.16'/%3E%3C/g%3E%3C/svg%3E"); }
.ah-ambient--stars {
  background-image: var(--ambient-stars);
  background-size: 100% 100%;
  background-repeat: no-repeat;
}

/* ---- Drift. Opacity and hue only, over half a minute, and it never
   reaches an extreme. If you can notice it happening, it is too strong. ---- */
@keyframes ah-ambient-breathe {
  from { opacity: calc(var(--ambient-bloom) * 0.72); }
  to   { opacity: var(--ambient-bloom); }
}
@keyframes ah-ambient-hue {
  from { filter: hue-rotate(-8deg); }
  to   { filter: hue-rotate(8deg); }
}

/* ---- The three ways this layer goes away. ---- */
@media (prefers-reduced-motion: reduce) {
  .ah-ambient,
  .ah-ambient::before,
  .ah-ambient::after { animation: none !important; }
}
@media (prefers-reduced-transparency: reduce) {
  .ah-ambient--grain::after,
  .ah-ambient--scan::before,
  .ah-ambient--bloom::before { display: none; }
}
@media (forced-colors: active) {
  .ah-ambient { display: none !important; }
}
