  /* ---------- multi-position card (collapsed strip on the feed card) ----
     Replaces the single bold role title when one posting bundles multiple
     positions under one card (e.g., 虚实传媒 7 岗位). Chip row keeps the
     fold scannable; the count + chevron promises an expand affordance. */
  /* Single-line, never-wrap chip strip. JS measures each chip and only
     renders chips that fully fit — anything that would be partially
     truncated gets dropped and the "…" overflow indicator is shown
     instead, so chip text never breaks mid-character. */
  .multi-pos-strip {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: 5px;
    margin-top: 2px;
    min-width: 0;
    overflow: hidden;
  }
  .multi-pos-chip {
    display: inline-flex;
    align-items: center;
    background: #f3f3f1;
    color: #0e0e10;
    border-radius: 5px;
    padding: 3px 8px;
    font-family: 'Geist', 'Noto Sans SC', sans-serif;
    font-size: 11.5px;
    font-weight: 500;
    line-height: 1.2;
    letter-spacing: -0.1px;
    white-space: nowrap;
    /* No per-chip ellipsis truncation: a chip is either rendered in full or
       not at all. The JS layout function in cend() picks which chips fit. */
    flex-shrink: 0;
  }
  /* Pathological fallback only: when even the first chip is wider than the
     available strip width, allow that single chip to truncate so the card
     doesn't end up empty. The .multi-pos-chip--squeeze modifier is added by
     JS in that edge case only. */
  .multi-pos-chip--squeeze {
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
    flex-shrink: 1;
    max-width: 100%;
  }
  /* Overflow indicator — quieter than a real chip (just the ellipsis glyph). */
  .multi-pos-chip-more {
    background: transparent;
    color: #94949c;
    padding: 4px 4px;
    letter-spacing: 0.05em;
    flex-shrink: 0;
    max-width: none;
  }
  .multi-pos-count {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    margin-left: auto;
    padding-left: 6px;
    font-family: 'Geist Mono', 'JetBrains Mono', ui-monospace, monospace;
    font-size: 11px;
    font-weight: 500;
    letter-spacing: 0.08em;
    color: #5a5a62;
    text-transform: uppercase;
    flex-shrink: 0;
    white-space: nowrap;
  }

  /* ---------- multi-position bottom sheet ---------------------------------
     Modal sheet that lists every position bundled in a multi-position card.
     On desktop it renders centered (max-width 640px); on mobile it slides
     up from the bottom and pins to the viewport edge with a drag handle.
     Accordion items inside the sheet expand mutually exclusively. */
  .sheet-card {
    background: #ffffff;
    border-radius: 22px;
    box-shadow: 0 30px 60px -20px rgba(14,14,16,0.45);
    width: 100%;
    max-width: 640px;
    max-height: 86vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }
  @media (max-width: 640px) {
    /* Backdrop default centers the card — override to anchor it to the
       bottom edge on phones for a true bottom-sheet feel. */
    .sheet-backdrop { align-items: flex-end; padding: 0; }
    .sheet-card { border-radius: 22px 22px 0 0; max-height: 92vh; }
  }
  /* Branded gradient band — a single soft tint pulled from the page's
     violet→pink axis. Earlier revisions layered two radial overlays plus
     a linear; the result read more like a marketing splash than an
     editorial header. One pink wash at low opacity is enough for the
     band to feel "branded" rather than flat-white.

     flex: 0 0 auto — locks the header at its natural height. Without
     this, when sheet-scroll has a lot of content (multi-position cards
     with 5+ chapters) the flex algorithm shrinks the header too, and
     overflow:hidden then clips the meta-pill row at the bottom edge. */
  .sheet-header {
    position: relative;
    display: flex;
    /* Center the logo against the whole text column (org · title · meta)
       so it doesn't read as top-heavy when a poster_type / company_tag
       pill (e.g. 组内直招) adds a third line under the title. */
    align-items: center;
    gap: 14px;
    padding: 20px 22px 18px;
    border-bottom: 1px solid rgba(14,14,16,0.06);
    overflow: hidden;
    flex: 0 0 auto;
    background:
      radial-gradient(140% 110% at 100% 0%, rgba(56,182,248,0.10), transparent 62%),
      linear-gradient(135deg, rgba(10,135,247,0.035) 0%, rgba(255,255,255,0) 70%);
  }
  /* Brand logo block — same treatment as the feed card's cover logo
     (white plate, soft shadow), scaled up so it anchors the gradient
     band. Falls back to a Fraunces monogram for un-cataloged orgs. */
  .sheet-logo {
    width: 56px;
    height: 56px;
    border-radius: 14px;
    background: #ffffff;
    border: 1px solid rgba(14,14,16,0.06);
    padding: 7px;
    object-fit: contain;
    flex-shrink: 0;
    box-shadow:
      0 8px 22px -10px rgba(14,14,16,0.22),
      0 2px 6px -2px rgba(14,14,16,0.08);
  }
  .sheet-logo-fallback {
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #0e0e10;
    color: #ffffff;
    border-color: transparent;
  }
  .sheet-logo-fallback svg { width: 60%; height: 60%; }
  /* First header line: company · team paired inline with the outbound
     pills (share / favourite / export). The org name flexes to fill the
     row and the pills cluster on the right; the role title and meta sit
     on their own lines below. */
  .sheet-org-row {
    display: flex;
    align-items: center;
    gap: 8px;
  }
  .sheet-org {
    flex: 1 1 auto;
    min-width: 0;
    font-family: 'Geist', 'Noto Sans SC', sans-serif;
    font-size: 15px;
    font-weight: 600;
    color: #3a3a42;
    letter-spacing: 0.01em;
    line-height: 1.35;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* Bigger, more confident title — Geist at 22px to carry the gradient
     band. Drops back to 19px on phones so it still fits a single line
     of "N 个岗位" plus the date suffix. */
  .sheet-title {
    font-family: 'Geist', 'Noto Sans SC', sans-serif;
    font-size: 22px;
    font-weight: 600;
    color: #0e0e10;
    letter-spacing: -0.5px;
    margin-top: 7px;
    line-height: 1.25;
  }
  /* Published-at suffix that trails the role title on desktop ("AI 产品经理 · 2026-05-28").
     Mobile media query below promotes this to its own line — the inline lockup
     wraps mid-token on a narrow column. */
  .sheet-title-date {
    margin-left: 8px;
    font-size: 12.5px;
    font-weight: 400;
    letter-spacing: 0;
    color: #94949c;
  }
  @media (max-width: 640px) {
    .sheet-header { padding: 18px 18px 16px; gap: 12px; }
    .sheet-logo { width: 48px; height: 48px; border-radius: 12px; padding: 6px; }
    .sheet-title { font-size: 19px; letter-spacing: -0.4px; }
  }
  .sheet-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 8px;
  }
  .sheet-close {
    margin-left: auto;
    /* The header centers the logo against the text column, but the close
       button should stay pinned to the top-right corner regardless of how
       tall that column gets. */
    align-self: flex-start;
    padding: 6px;
    border-radius: 8px;
    color: #5a5a62;
    background: transparent;
    border: none;
    cursor: pointer;
    transition: background 120ms ease, color 120ms ease;
    flex-shrink: 0;
  }
  .sheet-close:hover { background: #f3f3f1; color: #0e0e10; }

  /* Share / Favourite / Export pills share one silhouette (white pill,
     hairline border) so the header row reads as a consistent cluster.
     They sit on the first header line, inline with and to the right of
     the company name (which flexes to fill the row and pushes the pills
     to the right edge). The favourite's only divergence is the pink
     .is-saved accent below. */
  .sheet-share-btn,
  .sheet-export-btn,
  .sheet-fav-btn {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 6px 12px;
    border-radius: 999px;
    border: 1px solid rgba(14,14,16,0.12);
    background: #ffffff;
    color: #38383d;
    font-family: 'Geist', 'Noto Sans SC', sans-serif;
    font-size: 12.5px;
    font-weight: 500;
    cursor: pointer;
    transition: background 140ms ease, color 140ms ease, border-color 140ms ease;
  }
  /* Hover — a slight neutral highlight (not a full black invert) so the
     cue stays gentle and identical across all three pills. */
  .sheet-share-btn:hover,
  .sheet-export-btn:hover,
  .sheet-fav-btn:hover {
    background: #f3f3f1;
    color: #0e0e10;
    border-color: rgba(14,14,16,0.2);
  }
  /* Anchored outside .sheet-card via position:fixed so the popover isn't
     clipped by the sheet's overflow:hidden. top/right are set inline by
     toggleExportPopover() from the trigger button's viewport rect. The
     max-height keeps it within the viewport on small screens; the preview
     section then becomes the flex-shrinkable element. */
  .sheet-export-popover {
    position: fixed;
    z-index: 100;
    width: 380px;
    max-height: calc(100vh - 80px);
    background: #ffffff;
    border: 1px solid rgba(14,14,16,0.1);
    border-radius: 14px;
    box-shadow:
      0 24px 48px -16px rgba(14,14,16,0.28),
      0 4px 12px -4px rgba(14,14,16,0.10);
    padding: 14px;
    display: flex;
    flex-direction: column;
    gap: 12px;
  }
  /* Segmented control for the output shape — 卡片 vs 长图. Sits at the
     top of the popover; in card mode the style-chip row collapses
     since the floating-card design has no variants. */
  .sheet-export-modes {
    display: inline-flex;
    align-self: flex-start;
    padding: 3px;
    border-radius: 10px;
    background: rgba(14,14,16,0.05);
    gap: 2px;
  }
  .sheet-export-mode {
    appearance: none;
    border: none;
    background: transparent;
    color: #5a5a62;
    font-family: 'Geist', 'Noto Sans SC', sans-serif;
    font-size: 12.5px;
    font-weight: 500;
    padding: 5px 14px;
    border-radius: 7px;
    cursor: pointer;
    transition: background 140ms ease, color 140ms ease, box-shadow 140ms ease;
  }
  .sheet-export-mode:hover { color: #1a1a1d; }
  .sheet-export-mode.is-active {
    background: #ffffff;
    color: #1a1a1d;
    font-weight: 600;
    box-shadow:
      0 1px 2px rgba(14,14,16,0.08),
      0 0 0 1px rgba(14,14,16,0.04);
  }
  /* Card mode preview — the floater is 216×370 (taller than long mode's
     standard preview window), so we keep the same wrap chrome but allow
     vertical overflow. Horizontal centering is achieved by aligning the
     narrow iframe to the column center; vertical scroll kicks in only
     when the popover is squeezed by a short viewport, so the QR strip
     at the card's bottom stays reachable. */
  .sheet-export-popover.is-card-mode .sheet-export-preview-wrap {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    /* Prefer enough room for the whole floater (card 370 + body padding +
       breathing ≈ 410) but allow shrinking when the popover is squeezed by
       a short viewport — overflow-y keeps the QR strip reachable by
       scrolling inside the wrap, and min-height: 0 lets the download button
       below stay on-screen instead of being pushed past the viewport. */
    flex: 1 1 410px;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
  }
  /* In card mode the iframe doesn't need to host a 540px-wide long-card
     source — drop zoom and shrink to a card-sized viewport so the
     floater renders at its native 216×370 pixel size. */
  .sheet-export-popover.is-card-mode .sheet-export-preview-frame {
    width: 248px;
    /* 14px top + 370px card + 14px bottom (body padding from
       card-render.html's body[data-mode="card"] rule). Fixed so the
       iframe doesn't collapse to its default 760px when the live
       preview render is in flight — onPreviewLoad will refine if
       needed, but the floor stays correct from first paint. */
    min-height: 398px;
    zoom: 1;
  }
  .sheet-export-section { display: flex; flex-direction: column; gap: 6px; }
  /* Inline label shown above a chip row (e.g. "选择岗位" before the
     multi-position picker). Kept low-contrast so the chips themselves
     stay the visual anchor. */
  .sheet-export-section-label {
    font-family: 'Geist', 'Noto Sans SC', sans-serif;
    font-size: 11px;
    font-weight: 500;
    color: #5a5a62;
    letter-spacing: 0.02em;
  }
  /* Position-picker variant of the chip — no color-dot, slightly tighter
     padding since the label itself does all the work. */
  .sheet-export-chip--position {
    padding: 4px 12px;
    gap: 0;
  }
  /* Single horizontal row, side-scrollable. The mask gradient on the
     right/left edges fades the last visible chip into the surface so
     users get a wordless hint that there's more to scroll. Scrollbar
     hidden — drag / wheel / trackpad still work. */
  .sheet-export-styles {
    display: flex;
    flex-wrap: nowrap;
    gap: 6px;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -ms-overflow-style: none;
    /* Negative inline margin + matching padding lets the fade-mask sit
       flush against the popover edges instead of inside the gutter. */
    margin: 0 -14px;
    padding: 2px 14px;
    mask-image: linear-gradient(to right, transparent 0, #000 14px, #000 calc(100% - 14px), transparent 100%);
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 14px, #000 calc(100% - 14px), transparent 100%);
  }
  .sheet-export-styles::-webkit-scrollbar { display: none; }
  /* Each chip carries a small colored dot (via .sheet-export-chip-dot)
     hinting at the style's dominant surface tone — picker reads at a
     glance instead of being a row of identical gray pills. */
  .sheet-export-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px 4px 7px;
    border-radius: 999px;
    border: 1px solid rgba(14,14,16,0.08);
    background: #ffffff;
    color: #5a5a62;
    font-family: 'Geist', 'Noto Sans SC', sans-serif;
    font-size: 11.5px;
    font-weight: 500;
    cursor: pointer;
    flex-shrink: 0;
    white-space: nowrap;
    transition: background 140ms ease, color 140ms ease, border-color 140ms ease;
  }
  .sheet-export-chip-dot {
    width: 8px;
    height: 8px;
    border-radius: 999px;
    background: var(--chip-swatch, #c2c2c8);
    box-shadow: inset 0 0 0 1px rgba(14,14,16,0.10);
    flex-shrink: 0;
  }
  .sheet-export-chip:hover {
    background: #fafafa;
    border-color: rgba(14,14,16,0.18);
    color: #1a1a1d;
  }
  .sheet-export-chip.is-active {
    background: rgba(10,135,247,0.07);
    border-color: rgba(10,135,247,0.30);
    color: #074f9d;
    font-weight: 600;
  }
  .sheet-export-chip.is-active .sheet-export-chip-dot {
    box-shadow:
      inset 0 0 0 1px rgba(14,14,16,0.10),
      0 0 0 2px rgba(10,135,247,0.18);
  }
  /* Live preview iframe — card-render.html naturally renders at 540px wide
     in long mode. We use `zoom: 0.65` (rather than `transform: scale`) because
     zoom DOES affect layout — so the iframe's bounding box shrinks too, which
     lets the wrap actually compute scroll height and become a real scroll
     container. The iframe's intrinsic height is updated to the rendered card
     height via `onPreviewLoad` so there's no wasted blank space below the
     content. */
  .sheet-export-preview-wrap {
    position: relative;
    width: 100%;
    flex: 1 1 320px;
    /* min-height: 0 (not a fixed floor) so this preview is the element that
       absorbs a squeezed popover — it scrolls internally while the chips
       row and download button keep their natural size and stay visible. */
    min-height: 0;
    max-height: 420px;
    border-radius: 12px;
    border: 1px solid rgba(14,14,16,0.08);
    background: #faf9f6;
    overflow-y: auto;
    overflow-x: hidden;
    /* Hide scrollbar visually — its 15px gutter was clipping the right
       edge of the iframe (visible as asymmetric borders in styles like
       Mono). Scroll still works via mouse wheel / touch. */
    scrollbar-width: none;
  }
  .sheet-export-preview-wrap::-webkit-scrollbar { display: none; }
  .sheet-export-preview-frame {
    display: block;
    width: 540px;
    height: 760px;   /* initial — onPreviewLoad refits to content height */
    border: none;
    zoom: 0.65;
    pointer-events: none;
  }
  /* When the popover is squeezed by a short viewport, the chips section
     and download button keep their natural size and only the preview
     shrinks — preserving the affordance hierarchy. */
  .sheet-export-section { flex-shrink: 0; }
  .sheet-export-download { flex-shrink: 0; }
  .sheet-export-download {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 9px 14px;
    border-radius: 10px;
    background: #0e0e10;
    color: #ffffff;
    border: 1px solid #0e0e10;
    font-family: 'Geist', 'Noto Sans SC', sans-serif;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    transition: background 140ms ease, transform 140ms ease;
    margin-top: 2px;
  }
  .sheet-export-download:hover:not(:disabled) {
    background: #1a1a1d;
    transform: translateY(-1px);
  }
  .sheet-export-download:disabled { opacity: 0.5; cursor: wait; }
  /* Single scrolling region that contains both the shared-context block
     and the position list. Natural basis (auto) so the sheet's height
     matches its content for short cards; min-height: 0 lets it shrink
     under the parent's max-height: 86vh for tall multi-position cards
     (where it then takes over scrolling internally). The header above
     is pinned via flex: 0 0 auto, so any deficit goes here. */
  .sheet-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
  }
  .sheet-bottom-actions {
    display: flex;
    justify-content: center;
    padding: 0 20px 24px;
  }
  /* Editorial chapter list — replaces the prior accordion. Each
     position is a "chapter" with a Geist Mono numeral, role name, and
     optional salary pill, divided from its body by a hairline rule.
     All chapters are expanded by default so the sheet reads as a
     single magazine-style scroll instead of a click-to-reveal panel. */
  .sheet-chapters {
    padding: 22px 20px 24px;
    display: flex;
    flex-direction: column;
    gap: 36px;
  }
  .sheet-chapter {
    display: flex;
    flex-direction: column;
    gap: 14px;
  }
  /* Numeral lives in a 48px gutter on the left; the body below is
     indented to match, so the visual ribbon of "01 / 02 / 03" runs
     down the page like a book TOC. align-items: center because the
     numeral and the role name have different font sizes and line
     heights — baseline alignment leaves the small Mono digit
     visually drifting below the cap line of the name. */
  .sheet-chapter-head {
    display: flex;
    align-items: center;
    gap: 16px;
  }
  .sheet-chapter-no {
    font-family: 'Geist Mono', 'JetBrains Mono', ui-monospace, monospace;
    font-size: 13px;
    font-weight: 500;
    color: #94949c;
    letter-spacing: 0.04em;
    line-height: 1;
    flex-shrink: 0;
    width: 28px;
  }
  .sheet-chapter-name {
    flex: 1 1 auto;
    min-width: 0;
    font-family: 'Geist', 'Noto Sans SC', sans-serif;
    font-size: 16px;
    font-weight: 600;
    color: #0e0e10;
    letter-spacing: -0.3px;
    line-height: 1.3;
  }
  .sheet-chapter-salary {
    font-family: 'Geist Mono', 'JetBrains Mono', ui-monospace, monospace;
    font-size: 11.5px;
    font-weight: 500;
    color: #5a5a62;
    letter-spacing: 0.04em;
    padding: 3px 9px;
    border-radius: 999px;
    background: rgba(14,14,16,0.05);
    flex-shrink: 0;
  }
  .sheet-chapter-rule {
    height: 1px;
    background: rgba(14,14,16,0.08);
    border: none;
    margin: 0 0 0 48px;
  }
  .sheet-chapter-body {
    display: flex;
    flex-direction: column;
    gap: 14px;
    padding-left: 48px;
  }
  /* On phones the 48px gutter eats too much horizontal room — drop the
     numeral indent and let the body run flush-left, but keep the numeral
     in the head for the chapter mark. */
  @media (max-width: 640px) {
    .sheet-chapters { padding: 4px 18px 22px; gap: 30px; }
    .sheet-chapter-no { font-size: 12px; width: 24px; }
    .sheet-chapter-rule { margin-left: 0; }
    .sheet-chapter-body { padding-left: 0; }
  }
  .sheet-section { display: flex; flex-direction: column; gap: 8px; }
  /* Each section gets a small color dot keyed to its `data-tone` attr —
     enough to read as a color-coded TOC (粉=介绍, 紫=描述, 琥珀=要求,
     蓝=投递) without shouting. Label sits in mid-gray sans-serif so it
     stays a wayfinding cue rather than a heading; halos were removed —
     they made the dots read like LEDs on a control panel. */
  .sheet-section-label {
    font-family: 'Geist', 'Noto Sans SC', sans-serif;
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: 0.04em;
    color: #5a5a62;
    display: inline-flex;
    align-items: center;
    gap: 7px;
  }
  .sheet-section-label::before {
    content: '';
    width: 5px;
    height: 5px;
    border-radius: 999px;
    background: #c2c2c8;
    flex-shrink: 0;
  }
  .sheet-section[data-tone="pink"]    .sheet-section-label::before { background: #ec4899; }
  .sheet-section[data-tone="violet"]  .sheet-section-label::before { background: #8b5cf6; }
  .sheet-section[data-tone="amber"]   .sheet-section-label::before { background: #f59e0b; }
  .sheet-section[data-tone="emerald"] .sheet-section-label::before { background: #10b981; }
  .sheet-section[data-tone="blue"]    .sheet-section-label::before { background: #3b82f6; }
  .sheet-section-body {
    font-family: 'Geist', 'Noto Sans SC', sans-serif;
    font-size: 13.5px;
    line-height: 1.7;
    color: #38383d;
    white-space: pre-wrap;
    word-break: break-word;
  }
  /* Subtle neutral tint — sits between the white sheet card and the warm
     #FAF9F6 page background. Using a rgba shade of the text color keeps it
     perfectly hueless (no warm/cool cast), reading as "inset surface" rather
     than a tinted color. */
  .sheet-shared {
    background: rgba(14,14,16,0.028);
    border-bottom: 1px solid rgba(14,14,16,0.06);
    padding: 18px 20px;
    display: flex;
    flex-direction: column;
    gap: 14px;
  }
  /* Single-position sheet body — flat section list, no accordion / no
     gray inset since there's nothing to differentiate against. Same padding
     rhythm as .sheet-shared so the visual cadence matches. */
  .sheet-single-body {
    padding: 18px 20px;
    display: flex;
    flex-direction: column;
    gap: 16px;
  }
  .sheet-contact {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
  }
  .sheet-contact-code {
    flex: 1;
    min-width: 0;
    font-family: 'Geist Mono', 'JetBrains Mono', ui-monospace, monospace;
    font-size: 12px;
    color: #0e0e10;
    background: #ffffff;
    border: 1px solid rgba(14,14,16,0.08);
    padding: 6px 10px;
    border-radius: 6px;
    word-break: break-all;
    user-select: all;
  }

  /* Primary action — chunky enough to read as the primary path on the
     sheet, but the surface itself is a pale violet→pink wash (same axis
     as the header band, slightly stronger) so the volume stays low.
     The only saturated element is the 36×36 icon chip; the rest is
     dark text on a soft tint, hairline brand-tinted border. */
  .sheet-cta {
    display: flex;
    align-items: center;
    gap: 14px;
    width: 100%;
    padding: 14px 16px;
    margin-top: 2px;
    background: linear-gradient(135deg, #f0f7ff 0%, #e9f4fe 100%);
    color: #1a1a1d;
    border: 1px solid rgba(10,135,247,0.16);
    border-radius: 14px;
    cursor: pointer;
    text-align: left;
    box-shadow: 0 6px 16px -10px rgba(10,135,247,0.22);
    transition: transform 160ms cubic-bezier(0.22, 1, 0.36, 1),
                box-shadow 160ms ease,
                background 160ms ease,
                border-color 160ms ease;
  }
  .sheet-cta:hover {
    transform: translateY(-1px);
    background: linear-gradient(135deg, #e3f0fe 0%, #d9ecfd 100%);
    border-color: rgba(10,135,247,0.26);
    box-shadow: 0 10px 22px -10px rgba(10,135,247,0.28);
  }
  .sheet-cta:active { transform: translateY(0); }
  /* Soft brand-tinted plate, not a colored stamp — same hue as the
     button surface but a step stronger so the icon reads, without
     becoming the loudest element on the row. Stroke inherits color,
     so the paper-plane outline draws in brand violet. */
  .sheet-cta-icon {
    width: 32px;
    height: 32px;
    border-radius: 9px;
    background: rgba(10,135,247,0.10);
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    color: #0a87f7;
  }
  .sheet-cta-text {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
  }
  .sheet-cta-label {
    font-family: 'Geist', 'Noto Sans SC', sans-serif;
    font-size: 12.5px;
    font-weight: 500;
    letter-spacing: 0;
    color: #5a5a62;
  }
  .sheet-cta-contact {
    font-family: 'Geist Mono', 'JetBrains Mono', ui-monospace, monospace;
    font-size: 14px;
    font-weight: 500;
    letter-spacing: 0.02em;
    color: #1a1a1d;
    word-break: break-all;
    line-height: 1.3;
  }
  .sheet-cta-arrow {
    flex-shrink: 0;
    color: #94949c;
    transition: transform 160ms ease, color 160ms ease;
  }
  .sheet-cta:hover .sheet-cta-arrow { color: #5a5a62; }
  .sheet-cta:hover .sheet-cta-arrow { transform: translateX(2px); }
