@import url('https://fonts.googleapis.com/css2?family=Spectral:ital,wght@0,500;0,600;0,700;1,500&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@600;700&display=swap');

/* ============================================================================
   CIVIX — DESIGN SYSTEM  (warm-modern, geometric-civic)
   ----------------------------------------------------------------------------
   SINGLE SOURCE OF TRUTH. Every brand decision is a CSS custom property in the
   :root block below. To rebrand CIVIX you edit ONLY :root — nothing in the
   component layer hardcodes a brand hex, brand px, or font name.

   Warmth comes from colour + paper texture + soft elevation — not from a serif.

   Sections:
     1. TOKENS .......... :root — palette, semantics, type, space, radii,
                          shadows, borders, z-index, motion       (EDIT HERE)
     2. RESET + BASE .... element normalisation, body, paper grain
     3. TYPOGRAPHY ...... display / heading / text helpers
     4. LAYOUT .......... wrap, stack, row, spread
     5. COMPONENTS ...... .cv-card, .cv-btn--*, inputs/selects, .cv-pill--*,
                          .cv-seal (neutral civic seal), avatars, modal
     6. MAP TREATMENT ... .cv-map-duotone — teal/paper tile filter recipe
     7. UTILITIES ....... grid, reveal, motion-reduce

   Fonts (loaded via <link> on each page, or @import below):
     Space Grotesk — display / headings
     Public Sans   — body / UI
   ============================================================================ */

@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Public+Sans:ital,wght@0,300..800;1,400..600&display=swap');

/* ============================================================================
   1. TOKENS — the ONLY place to change brand identity
   ============================================================================ */
:root{

  /* ---- RAW PALETTE (founder-set official CIVIX colours) -------------------
     Change a hex here and it cascades everywhere via the semantic aliases. */
  --cv-ink:           #003135;   /* darkest teal — primary text            */
  --cv-deep-teal:     #024950;   /* deep teal — pressed / deep surfaces     */
  --cv-turquoise:     #0FA4AF;   /* PRIMARY brand + action                  */
  --cv-turquoise-br:  #3FC3CD;   /* brighter turquoise — glints / focus     */
  --cv-aqua:          #AFDDE5;   /* light aqua — chips / soft fills         */
  --cv-aqua-wash:     #DAF0F3;   /* palest aqua tint fill                   */
  --cv-aqua-wash-2:   #C3E5EB;   /* deeper aqua tint                        */
  --cv-terracotta:    #964734;   /* warm accent (terracotta), used sparing  */
  --cv-terracotta-dk: #7C3826;   /* pressed terracotta                      */
  --cv-terracotta-wash:#F3E3DD;  /* terracotta wash                         */

  /* turquoise DARKENED for white-text action surfaces (WCAG AA ≥4.5:1).
     --cv-turquoise (#0FA4AF) is the brand HUE — white text on it is only
     3.02:1 and FAILS AA, so it stays for accents/borders/rings/seal/pills/
     links (where text is NOT on top of it). For filled controls with white
     labels use --cv-turquoise-strong instead (white on it = 5.03:1, passes). */
  --cv-turquoise-strong:    #0A7B84;   /* white-on = 5.03:1 (AA pass)        */
  --cv-turquoise-strong-dk: #086D75;   /* hover, a touch darker = 6.08:1     */

  /* ---- CHANNEL TOKENS (space-separated RGB of each brand hex) -------------
     Let the component layer build brand shadows/glows/scrims via
     rgb(var(--cv-xxx-rgb) / <alpha>) so a single raw-hex rebrand also
     re-tints every shadow. Keep these in sync with the hexes above. */
  --cv-ink-rgb:          0 49 53;     /* #003135 */
  --cv-deep-teal-rgb:    2 73 80;     /* #024950 */
  --cv-turquoise-rgb:    15 164 175;  /* #0FA4AF */
  --cv-terracotta-rgb:   150 71 52;   /* #964734 */
  --cv-terracotta-dk-rgb:124 56 38;   /* #7C3826 */
  --cv-aqua-rgb:         175 221 229; /* #AFDDE5 */
  --cv-danger-rgb:       192 52 26;   /* #C0341A */
  --cv-band-2-rgb:       0 38 42;     /* #00262A — modal scrim wash */
  --cv-paper-rgb:        246 245 241; /* #F6F5F1 — paper-screen map lift */

  /* warm paper neutrals */
  --cv-paper:         #F6F5F1;   /* warm off-white app background           */
  --cv-paper-card:    #FBFAF6;   /* lifted card paper                       */
  --cv-paper-sink:    #ECEAE2;   /* recessed wells                          */
  --color-chrome:     #F2F4F3;   /* chrome surround (topbar/panels) — paper  *
                                  * family with a WHISPER of cool teal (blue  *
                                  * channel ≥ green so it leans aqua, never   *
                                  * beige). Was #F1EFE7 which read brown vs    *
                                  * --cv-paper. One-place rebrand seam; soft   *
                                  * white cards still lift off it.            */
  --cv-line:          #DEDDD4;   /* hairline on paper                       */
  --cv-line-strong:   #CBCABF;   /* stronger border                         */
  --cv-white:         #FFFFFF;

  /* dark band (mastheads / footers) */
  --cv-band:          #003135;
  --cv-band-2:        #00262A;
  --cv-on-band:       #DDEFF2;
  --cv-on-band-soft:  #8FBEC2;

  /* derived ink tints for secondary / tertiary text */
  --cv-ink-soft:      #2B5155;
  --cv-ink-faint:     #4E6B6E;   /* darkened from #5E7B7E for AA: 5.51:1 on
                                    surface, 5.27:1 on bg — soft but legible  */

  /* danger (destructive actions) */
  --cv-danger:        #C0341A;
  --cv-danger-dk:     #9A2814;
  --cv-danger-wash:   #FBEDEA;
  --cv-danger-line:   #E8C2BB;

  /* ---- SEMANTIC ALIASES --------------------------------------------------
     Components reference THESE, not the raw palette. Re-point an alias to
     re-skin a role without touching the raw colours. */
  --color-primary:        var(--cv-turquoise);        /* brand HUE: accents, borders, focus rings, seal, pills, links — NOT under white text */
  --color-primary-strong: var(--cv-turquoise-strong); /* action surface UNDER white text (AA ≥4.5:1) — filled buttons */
  --color-primary-strong-hover: var(--cv-turquoise-strong-dk);
  --color-primary-hover:  var(--cv-deep-teal);
  --color-primary-bright: var(--cv-turquoise-br);
  --color-accent:         var(--cv-terracotta);   /* warm pop, sparing       */
  --color-accent-hover:   var(--cv-terracotta-dk);

  --color-text:           var(--cv-ink);
  --color-text-soft:      var(--cv-ink-soft);
  --color-text-faint:     var(--cv-ink-faint);
  --color-on-primary:     var(--cv-white);        /* text on turquoise        */

  --color-bg:             var(--cv-paper);        /* app background           */
  --color-surface:        var(--cv-paper-card);   /* cards / panels           */
  --color-surface-sink:   var(--cv-paper-sink);   /* recessed wells / inputs  */

  --color-border:         var(--cv-line);
  --color-border-strong:  var(--cv-line-strong);
  --color-focus-ring:     var(--cv-aqua-wash);    /* soft focus halo          */

  --color-danger:         var(--cv-danger);
  --color-danger-hover:   var(--cv-danger-dk);

  /* status semantics (project lifecycle) */
  --status-published:     var(--cv-turquoise);    /* live / open for feedback */
  --status-draft:         var(--cv-ink-soft);     /* cool, NOT terracotta     */

  /* ---- TYPOGRAPHY --------------------------------------------------------- */
  --font-display: 'Space Grotesk', ui-sans-serif, system-ui, sans-serif;
  --font-body:    'Public Sans', ui-sans-serif, system-ui, sans-serif;
  --font-serif:   'Spectral', Georgia, 'Times New Roman', serif;
  --font-serif-display: 'Playfair Display', 'Spectral', Georgia, serif;

  --fw-light:   300;
  --fw-regular: 400;
  --fw-medium:  500;
  --fw-semibold:600;
  --fw-bold:    700;
  --fw-extra:   800;

  /* type scale (rem) */
  --fs-xs:   0.75rem;   /* 12 — captions / eyebrows  */
  --fs-sm:   0.875rem;  /* 14 — secondary / UI       */
  --fs-base: 1rem;      /* 16 — body                 */
  --fs-md:   1.125rem;  /* 18 — lead                 */
  --fs-lg:   1.35rem;   /* h3                        */
  --fs-xl:   1.85rem;   /* h2 floor                  */
  --fs-2xl:  2.5rem;    /* h1 floor                  */

  --lh-tight: 1.08;
  --lh-snug:  1.3;
  --lh-base:  1.55;

  /* ---- SPACING SCALE (8px base) ------------------------------------------ */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 22px;
  --sp-6: 32px;
  --sp-7: 48px;
  --sp-8: 64px;

  /* ---- RADII -------------------------------------------------------------- */
  --r-sm:   8px;
  --r:      14px;
  --r-lg:   22px;
  --r-xl:   30px;
  --r-pill: 999px;

  /* ---- BORDERS ------------------------------------------------------------ */
  --bw:        1px;       /* hairline           */
  --bw-strong: 1.5px;     /* input / ghost btn  */

  /* ---- SHADOWS (warm, layered, soft elevation set) ----------------------- */
  --sh-1:    0 1px 2px rgb(var(--cv-ink-rgb) / .05), 0 2px 6px rgb(var(--cv-ink-rgb) / .05);
  --sh-2:    0 2px 6px rgb(var(--cv-ink-rgb) / .06), 0 10px 24px rgb(var(--cv-ink-rgb) / .08);
  --sh-3:    0 6px 16px rgb(var(--cv-ink-rgb) / .08), 0 24px 60px rgb(var(--cv-ink-rgb) / .13);
  --sh-primary: 0 8px 24px rgb(var(--cv-deep-teal-rgb) / .22);   /* turquoise CTA lift  */

  /* ---- Z-INDEX LAYERS ----------------------------------------------------- */
  --z-base:    0;
  --z-content: 1;
  --z-masthead:5;
  --z-overlay: 50;
  --z-scrim:   60;
  --z-modal:   61;
  --z-toast:   90;

  /* ---- MOTION ------------------------------------------------------------- */
  --ease:        cubic-bezier(.2,.7,.2,1);
  --dur-fast:    .18s;
  --dur:         .25s;
  --dur-slow:    .5s;

  /* ---- LAYOUT ------------------------------------------------------------- */
  --maxw: 1180px;
}

/* ============================================================================
   2. RESET + BASE
   ============================================================================ */
.cv, .cv *, .cv *::before, .cv *::after{ box-sizing:border-box; }

/* When .cv is on <body>, establish the warm paper canvas + ink type.
   (Scoped to .cv so this file can be linked anywhere without hijacking
   pages that don't opt in.) */
body.cv{
  margin:0;
  font-family:var(--font-body);
  font-size:var(--fs-base);
  line-height:var(--lh-base);
  color:var(--color-text);
  background:var(--color-bg);
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
  background-image:
    radial-gradient(1200px 600px at 80% -10%, rgb(var(--cv-turquoise-rgb) / .07), transparent 60%),
    radial-gradient(900px 500px at -10% 10%, rgb(var(--cv-terracotta-rgb) / .04), transparent 55%);
  background-attachment:fixed;
}
body.cv img{ max-width:100%; display:block; }
body.cv a{ color:inherit; text-decoration:none; }
body.cv button{ font-family:inherit; }

/* faint paper grain — atmosphere, not noise */
body.cv::before{
  content:""; position:fixed; inset:0; pointer-events:none; z-index:var(--z-base);
  opacity:.5; mix-blend-mode:multiply;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.035'/%3E%3C/svg%3E");
}

/* ============================================================================
   3. TYPOGRAPHY
   ============================================================================ */
.cv-display{ font-family:var(--font-display); }

.cv h1,.cv h2,.cv h3,.cv h4,
.cv-h1,.cv-h2,.cv-h3{
  font-family:var(--font-display); font-weight:var(--fw-semibold);
  line-height:var(--lh-tight); letter-spacing:-.02em; color:var(--color-text); margin:0;
}
.cv h1,.cv-h1{ font-size:clamp(var(--fs-2xl),5.6vw,4.3rem); font-weight:var(--fw-bold); line-height:1.02; letter-spacing:-.03em; }
.cv h2,.cv-h2{ font-size:clamp(var(--fs-xl),3.3vw,2.8rem); font-weight:var(--fw-semibold); line-height:1.06; letter-spacing:-.025em; }
.cv h3,.cv-h3{ font-size:var(--fs-lg); font-weight:var(--fw-semibold); letter-spacing:-.015em; }
.cv p{ margin:0; }

.cv-lead{ font-size:var(--fs-md); color:var(--color-text-soft); line-height:1.6; }
.cv-eyebrow{
  font-family:var(--font-body); font-weight:var(--fw-bold);
  font-size:var(--fs-xs); letter-spacing:.22em; text-transform:uppercase;
  color:var(--color-primary-hover);
}
.cv-muted{ color:var(--color-text-faint); }

/* ============================================================================
   4. LAYOUT
   ============================================================================ */
.cv-wrap{ width:100%; max-width:var(--maxw); margin-inline:auto; padding-inline:var(--sp-6); position:relative; z-index:var(--z-content); }
.cv-stack{ display:flex; flex-direction:column; }
.cv-row{ display:flex; align-items:center; }
.cv-spread{ display:flex; align-items:center; justify-content:space-between; gap:var(--sp-4); }

/* ============================================================================
   5. COMPONENTS
   ============================================================================ */

/* ---- BRAND LOGO --------------------------------------------------------- */
/* The .cv-logo lockup = SVG mark + live "CIVIX" wordmark in Space Grotesk.
   Mark is civix-mark.svg (colored) on light grounds; .cv-logo--reverse swaps
   in civix-mark-reverse.svg for dark bands. Sized via --cv-logo-h (mark height,
   word scales from it). Favicon is civix-favicon.svg. */
.cv-logo{ display:inline-flex; align-items:center; gap:.40em; --cv-logo-h:30px; line-height:1; user-select:none; }
.cv-logo__mark{ flex:none; width:var(--cv-logo-h); height:var(--cv-logo-h);
  background:url("assets/civix-mark.svg") center/contain no-repeat; }
.cv-logo__word{ font-family:var(--font-display); font-weight:600; letter-spacing:.14em;
  text-transform:uppercase; color:var(--color-primary-hover);
  font-size:calc(var(--cv-logo-h) * 0.60); }
.cv-logo--reverse .cv-logo__mark{ background-image:url("assets/civix-mark-reverse.svg"); }
.cv-logo--reverse .cv-logo__word{ color:var(--cv-on-band); }
.cv-logo--sm{ --cv-logo-h:24px; }
.cv-logo--lg{ --cv-logo-h:40px; }
/* Words-only: hide the mark, leaving just the "CIVIX" wordmark. */
.cv-logo--wordmark .cv-logo__mark{ display:none; }

/* ---- CARDS -------------------------------------------------------------- */
.cv-card{
  background:var(--color-surface); border:var(--bw) solid var(--color-border);
  border-radius:var(--r-lg); box-shadow:var(--sh-1);
  transition:transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease), border-color var(--dur);
}
.cv-card--pad{ padding:var(--sp-5); }
.cv-card--lift:hover{ transform:translateY(-5px); box-shadow:var(--sh-2); border-color:var(--color-border-strong); }

/* ---- BUTTONS ------------------------------------------------------------ */
.cv-btn{
  display:inline-flex; align-items:center; justify-content:center; gap:var(--sp-2);
  font-family:var(--font-body); font-weight:var(--fw-bold); font-size:.98rem; letter-spacing:.005em;
  padding:13px 22px; border-radius:var(--r-pill); border:none; cursor:pointer;
  transition:transform var(--dur-fast) var(--ease), box-shadow var(--dur) var(--ease),
             background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur);
}
.cv-btn:focus-visible{ outline:3px solid var(--color-primary-bright); outline-offset:3px; }
.cv-btn:active{ transform:translateY(0); }
.cv-btn:disabled{ opacity:.45; cursor:not-allowed; transform:none !important; box-shadow:none; }
.cv-btn--full{ width:100%; }
.cv-btn--lg{ padding:16px 28px; font-size:1.05rem; }
.cv-btn--tiny{ padding:8px 14px; font-size:.82rem; }

/* filled, white-label CTA: uses the AA-safe strong action surface, NOT the raw brand hue.
   NOTE: `body.cv a{color:inherit}` (the global anchor reset) out-specifies a lone
   `.cv-btn--primary`, so anchor-based primary buttons (e.g. "Open my magic link") would
   inherit dark page text. Anchoring the colour to `.cv .cv-btn--primary` + an explicit
   `a.cv-btn--primary` keeps white-on-dark on BOTH <button> and <a>. SVG icons use
   `currentColor` (fill:none + stroke:currentColor in markup) so they follow the text colour. */
.cv .cv-btn--primary,
body.cv a.cv-btn--primary{ background:var(--color-primary-strong); color:var(--color-on-primary); box-shadow:var(--sh-primary); }
.cv-btn--primary svg{ color:inherit; }
.cv-btn--primary:hover{ transform:translateY(-2px); box-shadow:0 12px 30px rgb(var(--cv-deep-teal-rgb) / .3); background:var(--color-primary-strong-hover); }

.cv-btn--secondary{ background:var(--color-accent); color:var(--color-on-primary); box-shadow:0 8px 22px rgb(var(--cv-terracotta-dk-rgb) / .25); }
.cv-btn--secondary:hover{ transform:translateY(-2px); background:var(--color-accent-hover); }

.cv-btn--ghost{ background:transparent; color:var(--color-text); box-shadow:none; border:var(--bw-strong) solid var(--color-border-strong); }
.cv-btn--ghost:hover{ background:var(--color-surface); border-color:var(--color-primary); color:var(--color-primary-hover); transform:translateY(-2px); box-shadow:var(--sh-1); }

.cv-btn--danger{ background:var(--color-danger); color:var(--color-on-primary); box-shadow:0 8px 20px rgb(var(--cv-danger-rgb) / .22); }
.cv-btn--danger:hover{ background:var(--color-danger-hover); transform:translateY(-2px); }

/* arrow link */
.cv-link-arrow{ display:inline-flex; align-items:center; gap:7px; font-weight:var(--fw-bold); color:var(--color-primary-hover); cursor:pointer; }
.cv-link-arrow .arr{ transition:transform var(--dur) var(--ease); }
.cv-link-arrow:hover .arr{ transform:translateX(4px); }

/* ---- INPUTS / SELECTS (fully styled, never raw native) ------------------ */
.cv-label{ display:block; font-weight:var(--fw-bold); font-size:.86rem; color:var(--color-text); margin-bottom:7px; letter-spacing:.01em; }
.cv-hint{ font-size:.8rem; color:var(--color-text-faint); margin-top:6px; }

.cv-input, .cv-textarea, .cv-select{
  width:100%; font-family:inherit; font-size:var(--fs-base); color:var(--color-text);
  background:var(--color-surface); border:var(--bw-strong) solid var(--color-border-strong);
  border-radius:var(--r); padding:13px 15px;
  transition:border var(--dur-fast) var(--ease), box-shadow var(--dur) var(--ease), background var(--dur);
}
.cv-textarea{ min-height:84px; resize:vertical; line-height:1.5; }
.cv-input::placeholder, .cv-textarea::placeholder{ color:var(--color-text-faint); }
.cv-input:focus, .cv-textarea:focus, .cv-select:focus{
  outline:none; border-color:var(--color-primary); background:var(--cv-white);
  box-shadow:0 0 0 4px var(--color-focus-ring);
}

/* select: kill the native chevron, draw our own */
.cv-select{ appearance:none; -webkit-appearance:none; padding-right:42px; cursor:pointer; }
.cv-select-wrap{ position:relative; }
.cv-select-wrap::after{
  content:""; position:absolute; right:16px; top:50%; width:9px; height:9px;
  border-right:2px solid var(--color-text-soft); border-bottom:2px solid var(--color-text-soft);
  transform:translateY(-65%) rotate(45deg); pointer-events:none;
}

/* ---- STATUS PILLS ------------------------------------------------------- */
.cv-pill{
  display:inline-flex; align-items:center; gap:7px;
  font-family:var(--font-display); font-weight:var(--fw-semibold);
  letter-spacing:.12em; text-transform:uppercase; font-size:.66rem;
  padding:6px 12px 6px 11px; border-radius:var(--r-pill);
  box-shadow:0 1px 0 rgba(255,255,255,.6);
}
.cv-pill .dot{ width:7px; height:7px; border-radius:50%; flex:none; }

/* published = live, primary turquoise */
.cv-pill--published{ color:var(--color-primary-hover); background:var(--cv-aqua-wash); border:var(--bw-strong) solid var(--color-primary); }
.cv-pill--published .dot{ background:var(--status-published); box-shadow:0 0 0 3px var(--cv-aqua-wash-2); }

/* draft = cool + flat (deliberately NOT terracotta, so published pops) */
.cv-pill--draft{ color:var(--status-draft); background:var(--color-surface-sink); border:var(--bw-strong) solid var(--color-border-strong); }
.cv-pill--draft .dot{ background:var(--color-text-faint); }

/* ---- AVATARS ------------------------------------------------------------ */
.cv-avatar{
  width:30px; height:30px; border-radius:50%; display:grid; place-items:center; flex:none;
  font-weight:var(--fw-extra); font-size:.78rem; color:var(--cv-white); letter-spacing:.02em;
  background:linear-gradient(150deg,var(--color-primary),var(--color-primary-hover));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.3);
}

/* ============================================================================
   THE CIVIC SEAL  (.cv-seal)  —  NEUTRAL CORE, NO X MOTIF
   ----------------------------------------------------------------------------
   Warm civic-stamp / letterpress aesthetic, retained. The decorative
   "X-intersection" motif from the old mock is REMOVED entirely.

   The seal's CORE is implemented as ONE swappable element: .cv-seal__core.
   Swap that single element to rebrand the seal's centre — the default is a
   RADIAL CONVERGENCE NODE (short paths meeting at a centre point, never
   forming an X). Alternatives documented in DESIGN.md:
     - concentric rings  (set .cv-seal--rings)
     - "C" monogram      (set .cv-seal--mono, put a "C" in .cv-seal__core)
   ============================================================================ */
.cv-seal{
  position:relative; flex:none; isolation:isolate;   /* contain ::before/::after ink layers */
  /* (container-type:size removed — the only consumer of container-query units was
     the old HTML-text "C" monogram (50cqmin). The monogram is now an SVG <text>
     centred geometrically and scaled by its 0 0 100 100 viewBox, so no seal
     descendant needs cq units anymore.) */
  width:108px; height:108px; border-radius:50%;
  /* seal ink defaults to terracotta (warm register) — DESATURATED toward a mid
     grey so the stamp reads like faded vintage ink (still clearly terracotta).
     Scoped to the seal only; the brand tokens themselves stay full-strength. */
  --seal-ink:       color-mix(in srgb, var(--color-accent) 74%, #7a7a7a);
  --seal-ink-deep:  color-mix(in srgb, var(--color-accent-hover) 74%, #6a6a6a);
  color:var(--seal-ink-deep);
  background:
    radial-gradient(circle at 38% 32%, rgba(255,255,255,.55), transparent 55%),
    radial-gradient(circle at 50% 52%, rgb(var(--cv-ink-rgb) / .05), transparent 62%),
    var(--color-surface);
  /* letterpress: Variant A inset-mat border — asymmetric paper mats so the ink
     reads as ONE bordered stamp on a paper mat, not two parallel borders.
     (from disc edge inward) 3px paper lip → 2.5px ink → 6px inner paper gap,
     then the existing deboss + soft drop. Cumulative insets: 3 / 5.5 / 11.5px. */
  box-shadow:
    inset 0 0 0 3px   var(--color-surface),              /* OUTER paper margin (thin)   */
    inset 0 0 0 5.5px rgb(from var(--seal-ink) r g b / .85), /* ink border (2.5px) — slightly translucent so paper texture reads through */
    inset 0 0 0 11.5px var(--color-surface),             /* INNER paper gap (6px, wider) */
    inset 0 1.5px 3px rgb(var(--cv-ink-rgb) / .10),
    inset 0 -1px 2px rgba(255,255,255,.7),
    0 3px 10px rgb(var(--cv-ink-rgb) / .10);
  transform:rotate(-3.5deg);          /* one consistent slight stamp angle */
  display:grid; place-items:center;
  /* hand-pressed edge: a small feTurbulence displaced into every vector edge
     (ring, ring-text, core) so the disc reads as soaked-into-paper, not laser.
     scale is modest (1.8) so it stays tasteful from ~78px up to 280px. */
  filter:
    url("data:image/svg+xml;utf8,\
<svg xmlns='http://www.w3.org/2000/svg'>\
<filter id='cvSealInk' x='-12%' y='-12%' width='124%' height='124%'>\
<feTurbulence type='fractalNoise' baseFrequency='0.85 0.92' numOctaves='2' seed='7' stitchTiles='stitch' result='n'/>\
<feDisplacementMap in='SourceGraphic' in2='n' scale='1.8' xChannelSelector='R' yChannelSelector='G'/>\
</filter></svg>%23cvSealInk");
}
/* (b) ink-starve + paper-tooth layer — the rubber-stamp "dense rim / broken
   centre" tell. Paper-tooth grain multiplied over the surface, then a radial
   mask that keeps ink at the rim and starves it toward the middle. Sits BEHIND
   the SVG ring-text + core (z-index:-1) so the type is never masked away; the
   host's box-shadow ink ring is also untouched, so the disc edge stays crisp. */
.cv-seal::before{
  content:""; position:absolute; inset:0; z-index:-1; border-radius:50%;
  pointer-events:none;
  background-image:
    url("data:image/svg+xml;utf8,\
<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'>\
<filter id='cvSealGrain'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/>\
<feColorMatrix type='saturate' values='0'/></filter>\
<rect width='120' height='120' filter='url(%23cvSealGrain)' opacity='0.5'/></svg>"),
    repeating-linear-gradient(7deg,
      rgb(var(--cv-ink-rgb) / .035) 0 1px,
      transparent 1px 4px);
  background-size:120px 120px, auto;
  mix-blend-mode:multiply; opacity:.92;
  /* ink-starve: broken/light in the centre, dense at the rim — the rubber-stamp
     "faded middle / crisp inked edge" tell. Slightly stronger starve than the
     core so the texture reads convincingly even at the small (78px) sizes. */
  -webkit-mask:radial-gradient(118% 118% at 50% 47%, rgba(0,0,0,.22) 6%, rgba(0,0,0,.7) 46%, #000 70%);
          mask:radial-gradient(118% 118% at 50% 47%, rgba(0,0,0,.22) 6%, rgba(0,0,0,.7) 46%, #000 70%);
}
/* (c) double-strike ghost — a faint, slightly offset & counter-rotated second
   ink ring, the "stamp rocked on first contact" tell. Pure box-shadow on a
   ::after twin, no SVG restructure. */
.cv-seal::after{
  content:""; position:absolute; inset:0; z-index:-1; border-radius:50%;
  pointer-events:none;
  box-shadow:inset 0 0 0 2.5px rgb(var(--cv-ink-rgb) / .14);
  transform:translate(2.2px, 1.8px) rotate(.8deg);
  opacity:.65;
}
/* PUBLISHED / live register — same warm FORM, primary turquoise ink (DESATURATED
   toward grey so it reads as faded stamp ink, still clearly turquoise) */
.cv-seal--live{
  --seal-ink:       color-mix(in srgb, var(--color-primary) 72%, #7a7a7a);
  --seal-ink-deep:  color-mix(in srgb, var(--color-primary-hover) 72%, #6a6a6a);
}

/* ---- VOID / danger register --------------------------------------------------
   Same letterpress FORM + neutral convergence core as the default seal — only
   the ink shifts to an OFFICIAL-DOCUMENT red. We deliberately use the WARM-LEANING
   danger tokens (--cv-danger #C0341A / --cv-danger-dk #9A2814 — both carry a
   terracotta/oxide warmth, NOT a pure #FF0000 fire red) so the mark reads like a
   notarised "VOID" / "CANNOT BE UNDONE" rubber stamp rather than an alarm. Earns
   its warmth budget only at irreversible-destruction moments (delete watermark). */
.cv-seal--void{
  /* DESATURATED toward grey so the red reads as faded notary ink, not an alarm */
  --seal-ink:       color-mix(in srgb, var(--color-danger) 72%, #7a7a7a);
  --seal-ink-deep:  color-mix(in srgb, var(--color-danger-hover) 72%, #6a6a6a);
}

/* DANGER WATERMARK presentation: a static, debossed seal sitting BEHIND a
   type-to-confirm field. Never animated (animation on destruction reads
   celebratory). Decorative only — always pair with aria-hidden="true". The
   host positions it; here we strip the seal's lifted paper chrome so it reads
   as an ink stamp pressed INTO the page, then hold it at a low resting opacity
   that strengthens (via .is-armed) the instant the confirm input matches. */
.cv-seal--watermark{
  background:none;                 /* no paper disc — pure debossed ink */
  box-shadow:
    inset 0 0 0 2px var(--seal-ink),
    inset 0 1px 2px rgb(var(--cv-ink-rgb) / .12);
  opacity:.10;                     /* faint resting watermark (~10%) */
  transition:opacity var(--dur) var(--ease);
}
.cv-seal--watermark.is-armed{ opacity:.20; }  /* rises to ~20% when delete arms */
@media (prefers-reduced-motion: reduce){
  .cv-seal--watermark{ transition:none; }
}

/* ---- NEUTRAL / draft register ------------------------------------------------
   The "before" twin of the live published seal. Shown above the "Not yet public"
   message on a draft share view, where a warm fully-inked disc would wrongly read
   as celebratory. So we shift the ink to a calm, cool grey-teal (--cv-ink-faint —
   neither pure black nor terracotta) and strip the lifted paper chrome the same
   way --watermark does, leaving a debossed inset-ink stamp pressed INTO the page.
   Unlike --watermark this is shown prominently, so it stays at full opacity. */
.cv-seal--neutral{
  --seal-ink:      var(--cv-ink-faint);
  --seal-ink-deep: var(--cv-ink-faint);
  background:none;                  /* no paper disc — pure debossed ink */
  box-shadow:
    inset 0 0 0 2px var(--seal-ink),
    inset 0 1px 2px rgb(var(--cv-ink-rgb) / .12);
}

/* ring text rendered as SVG textPath sits absolutely over the core */
.cv-seal__svg{ position:absolute; inset:0; width:100%; height:100%; overflow:visible; }
/* curved ring lettering — sized to read as authentic stamped type. A confident
   bold weight + open-but-even tracking makes the curve scan as PRESSED letters,
   not a thin vector outline; it stays crisp at 78px and presses heavier at 280px.
   font-size is in viewBox user-units (viewBox 0 0 108 108) so it scales with the
   disc automatically — no per-size override needed for legibility. */
.cv-seal__ring-text{
  font-family:var(--font-serif); font-weight:var(--fw-bold);
  /* Spectral (a transitional serif) sets marginally wider than the geometric
     sans at equal tracking, so trim letter-spacing a hair for even spacing
     around the ring while staying legible at 78px. */
  font-size:9.1px; letter-spacing:1.4px; fill:var(--seal-ink-deep); text-transform:uppercase;
  /* INK PRESSED-INTO-PAPER: tiny translucency so the paper grain reads through
     the ring lettering (not faded — just enough to feel inked, not printed). */
  fill-opacity:.85;
}
/* The Variant-A inset-mat border (box-shadow) IS the ring now; this redundant
   thin SVG ring duplicated/overlapped it, so it is hidden on every seal at once.
   (Left in markup as a no-op for back-compat; CSS hide is least invasive.) */
.cv-seal__ring-line{ display:none; }

/* ---- THE SWAPPABLE CORE (default: radial convergence node) -------------- */
/* A small set of short strokes converging on a centre node — civic "all paths
   meet here" symbolism. NO diagonal cross / X. Swap this element to rebrand. */
.cv-seal__core{ position:relative; width:40%; height:40%; display:grid; place-items:center; color:var(--seal-ink-deep);
  /* INK PRESSED-INTO-PAPER: slight translucency on the whole core (icon +
     monogram letter inherit this) so the paper texture reads through them. */
  opacity:.9; }
.cv-seal__core svg{ width:100%; height:100%; display:block; overflow:visible; }
.cv-seal__core .spoke{ stroke:var(--seal-ink-deep); stroke-width:2; stroke-linecap:round; }
.cv-seal__core .node{ fill:var(--seal-ink); }
.cv-seal__core .node-ring{ fill:none; stroke:var(--seal-ink); stroke-width:1.4; opacity:.7; }

/* ---- ICON core variant -------------------------------------------------- */
/* A Tabler 24x24 stroke icon as the seal core. The base rule
   `.cv-seal__core svg{ width:100%; height:100% }` already scales the svg to the
   (40%-of-disc) core box, and `.cv-seal__core{ color:var(--seal-ink-deep) }`
   sets the ink — the icon uses stroke="currentColor" so it inherits that ink.
   Tabler 24x24 icons nearly fill their viewBox, so a slight inset (.78) gives
   the icon breathing room from the ring. */
.cv-seal__icon{ transform: scale(.78); transform-origin: 50% 50%; }

/* "C" monogram core variant (set .cv-seal--mono on the seal, drop an SVG <text>
   "C" into .cv-seal__core — see shared markup contract below).
   A refined wax-seal monogram: heavy confident display weight, optically sized to
   fill the core without crowding the ring. Centring is now done GEOMETRICALLY by
   the SVG itself (text-anchor:middle on x=50 + dominant-baseline:central on y=50
   inside the 0 0 100 100 viewBox), so it is metric-independent — perfectly centred
   at 78px, 108px AND 280px with ZERO per-size nudges. No translate hack. The old
   HTML-text approach drifted low+left because Playfair's font metrics made the
   text line-box centring unreliable; SVG geometry removes that fragility entirely.

   Shared markup contract this rule targets:
     <span class="cv-seal__core">
       <svg class="cv-seal__mono" viewBox="0 0 100 100" aria-hidden="true">
         <text x="50" y="50" text-anchor="middle" dominant-baseline="central">C</text>
       </svg>
     </span>
   The base rule `.cv-seal__core svg{ width:100%; height:100%; overflow:visible }`
   makes this svg fill the (40%-of-disc) core box and scale with the disc. */
.cv-seal--mono .cv-seal__mono text{
  /* Playfair Display — a high-contrast Didone with dramatic bracketed serifs:
     unmistakably elegant-serif, the right register for a big wax-seal monogram.
     RING TEXT stays Spectral (--font-serif); only the monogram goes display. */
  font-family:var(--font-serif-display); font-weight:700;
  /* font-size is in USER UNITS within the 0 0 100 100 viewBox; the svg IS the
     core box (40% of disc) with overflow visible, so 104 lets Playfair's cap
     (~0.7×em → ~73 units) fill the core confidently like a wax-seal monogram
     without crowding the ring. Scales with the disc automatically — no cqmin,
     no per-page override. */
  font-size:104px;
  fill:var(--seal-ink-deep);
  /* geometric centring — metric-independent, size-independent, no nudges */
  text-anchor:middle;          /* horizontal: centres the glyph on x=50 */
  dominant-baseline:central;   /* vertical: centres the em's middle on y=50 */
  /* subtle debossed letterpress: a light paper-highlight catches just below the
     glyph so it reads pressed INTO the disc. Kept minimal (single soft drop) so
     it doesn't muddy Playfair's thin high-contrast terminals. */
  filter:drop-shadow(0 1px 0 rgb(var(--cv-paper-rgb) / .8));
  transform: translateY(-3px);   /* nudge the cap up a tinge (SVG user units in the 0 0 100 100 viewBox) */
}

/* size variants */
.cv-seal--sm{ width:78px; height:78px; }
/* sm 78px: fixed-px Variant-A border reaches relatively further in, so pull the
   ring type smaller + tighter to keep a clear gap from the border at this size. */
.cv-seal--sm .cv-seal__ring-text{ font-size:7.6px; letter-spacing:0.8px; }
/* xs = EMBLEM mode. Below ~40px the curved ring-text (a textPath wrapped around a
   ~12px-radius path) is physically unreadable at any weight, and the double-inset
   disc ring eats most of the radius into a thick blob. So xs drops the text entirely
   and reads as a clean EMBLEM: the core convergence mark + ONE thin proportional
   ring. Every real small placement (topbar owner badge, status chip, invite, owner
   card, share topbar live) overrides width below — 62px is just a sane default. */
.cv-seal--xs{
  width:62px; height:62px;
  box-shadow:
    inset 0 0 0 1.5px var(--seal-ink),       /* one thin proportional ink ring */
    0 1px 2px rgb(var(--cv-ink-rgb) / .12);  /* soft drop — no debossed double ring */
}
.cv-seal--xs .cv-seal__ring-text,
.cv-seal--xs .cv-seal__ring-line{ display:none; }   /* text-free emblem */
.cv-seal--xs .cv-seal__core{ width:56%; height:56%; }  /* core fills the disc */

/* ============================================================================
   THE CACHET  (.cv-cachet)  —  SMALL RUBBER-STAMP SIBLING OF THE ROUND SEAL
   ----------------------------------------------------------------------------
   The round .cv-seal stays for LARGE ceremonial marks. The Cachet is the
   small-chip form for every badge/pill in the product (Owner, Published,
   Open-for-comment, Member, danger VOID). It is a clerk's rectangular rubber
   date-stamp: a tilted, double-deckle inked box where THE WORD IS THE MARK —
   no convergence-node, no X motif, no ring text. Just a small left insignia
   glyph + an uppercase letterpress word, inked in one swappable register.

   ONE local var pair (--ink / --ink-deep) drives EVERYTHING — border, word,
   tick, focus ring. Swap the register modifier to recolour the whole stamp.

   INK-STARVE APPROACH: the rubber-stamp "faded center / crisp rim" tell is
   rendered on a ::before ink+paper LAYER, NOT on the host. The radial mask
   that starves the ink unavoidably eats into whatever it is applied to, so
   masking the host would chew the double-deckle border. By putting paper
   tooth + the starve mask on ::before and keeping the deckle box-shadow on
   the (unmasked) host, the border stays crisp while the inked fill fades at
   the rim — exactly how a real rubber stamp presses.

   Canonical markup:
     <span class="cv-cachet cv-cachet--owner" role="img"
           aria-label="Owner" title="Owner">
       <i class="ti ti-crown cv-cachet__tick" aria-hidden="true"></i>
       <span class="cv-cachet__word">OWNER</span>
     </span>
   ============================================================================ */
.cv-cachet{
  /* default ink register = terracotta (warm). Modifiers swap this pair. */
  --ink:      var(--color-accent);
  --ink-deep: var(--color-accent-hover);
  --press:0;                       /* 0 = resting tilt · 1 = slammed flat */

  position:relative; isolation:isolate;   /* contain the ::before ink layer */
  display:inline-flex; align-items:center; gap:5px; flex:none;
  height:24px; padding:0 9px; border-radius:var(--r-sm);
  vertical-align:middle; overflow:hidden;

  /* letterpress type — the word carries the mark */
  font-family:var(--font-display); font-weight:var(--fw-bold);
  font-size:10px; letter-spacing:1.5px; text-transform:uppercase; line-height:1;
  color:var(--ink);

  /* double deckle border + top highlight + bottom deboss — kept on the
     UNMASKED host so the rim stays crisp */
  box-shadow:
    0 0 0 1.5px var(--color-surface),          /* paper mat: separates the colored edge from any background */
    inset 0 0 0 2px var(--ink),
    inset 0 0 0 4px var(--color-surface),
    inset 0 1px 0 rgba(255,255,255,.7),
    inset 0 -1px 1px rgb(var(--cv-ink-rgb) / .12);

  /* tilt — flattens toward 0deg as --press rises (hover / armed) */
  transform:rotate(calc(-2.5deg * (1 - var(--press))));
  transition:transform var(--dur-fast) var(--ease),
             filter    var(--dur-fast) var(--ease);
}
/* ink + paper layer: paper tooth over the surface, then the radial
   ink-starve mask so the centre is fully inked and the rim fades. Border
   lives on the host above, so this mask never touches the deckle. */
.cv-cachet::before{
  content:""; position:absolute; inset:0; z-index:-1; border-radius:inherit;
  background:
    repeating-linear-gradient(8deg, rgb(var(--cv-ink-rgb) / .03) 0 1px, transparent 1px 3px),
    var(--color-surface);
  background-blend-mode:multiply, normal;
  -webkit-mask:radial-gradient(125% 160% at 50% 50%, #000 30%, rgba(0,0,0,.80) 100%);
          mask:radial-gradient(125% 160% at 50% 50%, #000 30%, rgba(0,0,0,.80) 100%);
}
/* left insignia glyph — Tabler webfont glyph, sized via font-size */
.cv-cachet__tick{
  flex:none; font-size:10px; line-height:1; color:var(--ink);
  position:relative; top:.5px;          /* optical baseline alignment */
}
/* the word — inherits cachet type, tiny optical baseline nudge */
.cv-cachet__word{ position:relative; top:.5px; }

/* ---- INK REGISTERS (one var swap recolours border + word + tick) -------- */
.cv-cachet--owner,
.cv-cachet--member{ --ink:var(--color-accent);  --ink-deep:var(--color-accent-hover); }  /* terracotta (default) */
.cv-cachet--live,
.cv-cachet--open{   --ink:var(--color-primary); --ink-deep:var(--color-primary-hover); }  /* turquoise */
.cv-cachet--void{   --ink:var(--color-danger);  --ink-deep:var(--color-danger-hover); }   /* danger red */

/* ---- INTERACTION: "fresh press" on hover/focus -------------------------- */
.cv-cachet:hover,
.cv-cachet:focus-visible{ --press:1; filter:saturate(1.08); }   /* flatten + deepen ink */
.cv-cachet:focus-visible{ outline:2px solid var(--ink); outline-offset:2px; }

/* ---- DANGER ARM PATTERN: VOID slams down when confirm matches ----------- */
/* rests faded + extra-tilted; .is-armed (JS-toggled on type-to-confirm
   match) slams it flat, opaque and fully saturated — the stamp coming down. */
.cv-cachet--void{ opacity:.6; transform:rotate(-3deg); }
.cv-cachet--void.is-armed{ opacity:1; --press:1; filter:saturate(1.15); }

/* ---- REDUCED MOTION: keep the opacity step, drop all motion -------------- */
@media (prefers-reduced-motion: reduce){
  .cv-cachet{ transform:none; transition:none; }
  .cv-cachet--void{ transform:none; }                 /* no extra tilt */
  .cv-cachet--void.is-armed{ transform:none; }        /* opacity still steps, instantly */
}

/* ---- MODAL -------------------------------------------------------------- */
.cv-scrim{ position:fixed; inset:0; background:rgb(var(--cv-band-2-rgb) / .45); backdrop-filter:blur(3px); z-index:var(--z-scrim); opacity:0; pointer-events:none; transition:opacity var(--dur) var(--ease); }
.cv-scrim.open{ opacity:1; pointer-events:auto; }
.cv-modal{
  position:fixed; z-index:var(--z-modal); left:50%; top:50%; transform:translate(-50%,-46%) scale(.98);
  width:min(560px,92vw); max-height:88vh; overflow:auto; opacity:0; pointer-events:none;
  background:var(--color-surface); border:var(--bw) solid var(--color-border); border-radius:var(--r-xl);
  box-shadow:var(--sh-3); transition:opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.cv-modal.open{ opacity:1; pointer-events:auto; transform:translate(-50%,-50%) scale(1); }
.cv-modal__head{ padding:24px 26px 14px; border-bottom:var(--bw) solid var(--color-border); display:flex; justify-content:space-between; align-items:flex-start; gap:14px; }
.cv-modal__body{ padding:22px 26px; display:flex; flex-direction:column; gap:18px; }
.cv-modal__foot{ padding:18px 26px 24px; display:flex; gap:12px; justify-content:flex-end; border-top:var(--bw) solid var(--color-border); }

/* ---- DARK BAND (mastheads / footers) ------------------------------------ */
.cv-band{ background:linear-gradient(180deg,var(--cv-band),var(--cv-band-2)); color:var(--cv-on-band); position:relative; z-index:var(--z-content); }
.cv-band a{ color:var(--cv-on-band); }
.cv-band .cv-muted{ color:var(--cv-on-band-soft); }

/* ============================================================================
   6. THE MAP TREATMENT  (.cv-map-duotone)
   ----------------------------------------------------------------------------
   Brand-tints raw OSM raster tiles into the teal/paper duotone. The proven
   filter recipe from the mock, retuned to #0FA4AF turquoise / #024950 deep.
   Apply .cv-map-duotone to the tile container; the ::before/::after seal the
   colour cohesion. Leaflet usage: put it on the .leaflet-tile-pane wrapper or
   a wrapping element — it filters child tiles.
   ============================================================================ */
.cv-map-duotone{ position:relative; overflow:hidden; background:#dceaec; isolation:isolate; }

/* the duotone filter applied to the tiles themselves */
.cv-map-duotone .cv-tiles,
.cv-map-duotone.cv-map-duotone--self{
  /* NOTE: this hue-rotate angle (135deg) is HAND-TUNED to the turquoise brand
     hue (#0FA4AF). It CANNOT be derived from a hex variable — if the brand hue
     is rebranded away from turquoise, this filter angle (and saturate amount)
     must be re-tuned by eye. This is the one documented colour exception that a
     raw-hex rebrand does NOT cascade to (see DESIGN.md). */
  filter:
    grayscale(1) brightness(1.08) contrast(.9)
    sepia(.55) hue-rotate(135deg) saturate(2.0);
}
.cv-map-duotone .cv-tiles img{
  display:block;
  /* fallback on tile fail — aqua wash derived from the brand aqua channel */
  background:linear-gradient(135deg, rgb(var(--cv-aqua-rgb) / .85), rgb(var(--cv-aqua-rgb) / 1));
}

/* turquoise multiply seals colour cohesion */
.cv-map-duotone::before{
  content:""; position:absolute; inset:0; z-index:1; pointer-events:none;
  background:linear-gradient(160deg, rgb(var(--cv-turquoise-rgb) / .38), rgb(var(--cv-deep-teal-rgb) / .30) 55%, rgb(var(--cv-ink-rgb) / .42));
  mix-blend-mode:multiply;
}
/* paper screen lifts highlights back to warm, prevents murk */
.cv-map-duotone::after{
  content:""; position:absolute; inset:0; z-index:2; pointer-events:none;
  background:
    radial-gradient(120% 90% at 50% 30%, rgb(var(--cv-paper-rgb) / .20), transparent 60%),
    linear-gradient(0deg, rgb(var(--cv-paper-rgb) / .12), transparent 40%);
  mix-blend-mode:screen;
}
.cv-map-duotone .cv-map-content{ position:relative; z-index:3; height:100%; }

/* ============================================================================
   7. UTILITIES + MOTION
   ============================================================================ */
.cv-grid{ display:grid; gap:20px; }
.cv-g-2{ grid-template-columns:repeat(2,1fr); }
.cv-g-3{ grid-template-columns:repeat(3,1fr); }
@media(max-width:920px){ .cv-g-3{ grid-template-columns:repeat(2,1fr); } }
@media(max-width:620px){ .cv-g-2,.cv-g-3{ grid-template-columns:1fr; } }

.cv-reveal{ opacity:0; transform:translateY(16px); animation:cv-reveal .8s var(--ease) forwards; }
@keyframes cv-reveal{ to{ opacity:1; transform:none; } }
.cv-d1{animation-delay:.05s}.cv-d2{animation-delay:.14s}.cv-d3{animation-delay:.24s}
.cv-d4{animation-delay:.34s}.cv-d5{animation-delay:.44s}.cv-d6{animation-delay:.54s}

@media (prefers-reduced-motion: reduce){
  .cv *,.cv *::before,.cv *::after{
    animation-duration:.001ms !important; animation-iteration-count:1 !important;
    transition-duration:.001ms !important; scroll-behavior:auto !important;
  }
}

/* ============================================================================
   PROJECT-SEAL PICKER — shared anchored popover (CivixProjectSeal.openPicker).
   On-token surface; 4×3 grid of full ring-word seals; hover-lift + selected
   ring. position:fixed so it escapes any scroll/overflow clip; placed by JS.
   Loaded on every page (civix-design.css), works in the editor + both modals.
   ============================================================================ */
.cv-projseal-picker{
  /* FIX 6: above any anchor's modal/scrim context on every page — editor
     .contextual-modal is 1200 and .modal-overlay scrims reach 10000/100000,
     so the picker popover must sit clearly above all of them. */
  position:fixed; z-index:100001;
  background:var(--color-surface);
  border:var(--bw) solid var(--color-border);
  border-radius:var(--r-lg);
  box-shadow:var(--sh-2);
  padding:12px 14px 14px;
  width:max-content; max-width:min(360px, calc(100vw - 20px));
}
.cv-projseal-picker__eyebrow{
  font-family:var(--font-display); font-weight:var(--fw-semibold);
  font-size:.66rem; letter-spacing:.1em; text-transform:uppercase;
  color:var(--color-primary-hover); margin:0 2px 10px;
}
.cv-projseal-picker__grid{
  display:grid; grid-template-columns:repeat(4, 1fr); gap:8px;
}
.cv-projseal-picker__tile{
  display:grid; place-items:center; padding:6px; cursor:pointer;
  border-radius:var(--r); background:transparent;
  border:var(--bw) solid transparent;
  transition:transform var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.cv-projseal-picker__tile .cv-projseal{ display:inline-flex; line-height:0; pointer-events:none; }
.cv-projseal-picker__tile:hover{
  transform:translateY(-2px);
  border-color:var(--color-border-strong);
  box-shadow:var(--sh-1);
}
.cv-projseal-picker__tile.is-selected{
  border-color:var(--color-primary);
  box-shadow:0 0 0 3px var(--color-focus-ring);
}
.cv-projseal-picker__tile:focus-visible{
  outline:3px solid var(--color-primary-bright); outline-offset:2px;
}

/* Editor-header seal made pickable (owner only) — subtle clickable affordance.
   The base .left-project-seal layout lives in editor-skin.css; this only adds the
   interactive hint, applied via the .left-project-seal--pickable flag set by
   app.js wireProjectSealPicker(). */
.left-project-seal--pickable{
  cursor:pointer; border-radius:var(--r-sm);
  transition:transform var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.left-project-seal--pickable:hover{ transform:translateY(-1px); box-shadow:var(--sh-1); }
.left-project-seal--pickable:focus-visible{ outline:3px solid var(--color-primary-bright); outline-offset:2px; }
