/* ============================================================================
 * components.css — reusable UI: buttons, pills, badges, icons, cards, stat
 * tiles, trust cards, Lumina badge/orb, media frames, drop-zones, forms,
 * cookie banner, and the claim-gate presentation. Ported and adapted from
 * the marketing deck's screens stylesheet (slide-gated animations replaced with
 * reveal-triggered / ambient motion). Depends on tokens.css + base.css.
 * ==========================================================================*/

/* ---------------------------------------------------------------------------
 * BUTTONS
 * ------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 12px 20px;
  border-radius: 12px;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: var(--fs-sm);
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  border: 1.5px solid transparent;
  transition:
    transform var(--dur-fast) var(--ease-pop),
    box-shadow var(--dur) var(--ease),
    background var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease);
}
.btn:active {
  transform: scale(0.97);
}
.btn-sm {
  padding: 9px 15px;
  font-size: var(--fs-xs);
  border-radius: 10px;
}
.btn-lg {
  padding: 15px 26px;
  font-size: var(--fs-body);
  border-radius: 14px;
}
.btn-block {
  width: 100%;
}
/* .btn-sm computes to 34px, and it carries the header CTAs and both cookie
   consent buttons. Raise it to the 44px touch floor only where the pointer is
   coarse, so the desktop proportions are untouched. */
@media (pointer: coarse) {
  .btn-sm { min-height: 44px; }   /* was 34px */
  .btn { min-height: 44px; }      /* was 41px */
  /* 24px tall as a bare text row, and it is the card's only affordance. */
  .pmcard .pmlink { min-height: 44px; align-items: center; }
}

.btn-primary {
  background: var(--grad-blue);
  color: #fff;
  box-shadow: var(--shadow-lg);
}
.btn-primary:hover {
  color: #fff;
  transform: translateY(-1px);
  box-shadow: 0 26px 56px -18px rgba(24, 104, 219, 0.44);
}

.btn-ghost {
  background: #fff;
  color: var(--ink);
  border-color: var(--line);
  box-shadow: var(--shadow-sm);
}
.btn-ghost:hover {
  color: var(--ink);
  border-color: var(--ink-faint);
  transform: translateY(-1px);
}

.btn-quiet {
  background: var(--surface-2);
  color: var(--ink-soft);
}
.btn-quiet:hover {
  background: var(--surface-3);
  color: var(--ink);
}

.btn-lumina {
  /* pink end darkened from #e91e63 (white label was 4.35:1) */
  background: linear-gradient(135deg, #e6175d, #9c27b0);
  color: #fff;
  box-shadow: 0 14px 34px -12px rgba(156, 39, 176, 0.6);
}
.btn-lumina:hover {
  color: #fff;
  transform: translateY(-1px);
}

.btn-link {
  color: var(--blue-deep);
  font-weight: 800;
}
.btn-link .arw {
  transition: transform var(--dur) var(--ease);
}
.btn-link:hover .arw {
  transform: translateX(4px);
}

.btn-row {
  display: flex;
  align-items: center;
  gap: var(--s3);
  flex-wrap: wrap;
}

/* on dark surfaces */
.on-dark .btn-ghost {
  background: rgba(255, 255, 255, 0.08);
  color: #fff;
  border-color: rgba(255, 255, 255, 0.2);
  box-shadow: none;
}
.on-dark .btn-ghost:hover {
  background: rgba(255, 255, 255, 0.16);
  color: #fff;
}

/* ---------------------------------------------------------------------------
 * PILLS / EYEBROW PILL / BADGES
 * ------------------------------------------------------------------------- */
.eyebrow-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  padding: 7px 14px;
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--ink-soft);
  box-shadow: var(--shadow-sm);
}
.eyebrow-pill .dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--green);
  flex: none;
}
.on-dark .eyebrow-pill {
  background: rgba(255, 255, 255, 0.08);
  border-color: rgba(255, 255, 255, 0.16);
  color: #fff;
}

.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  padding: 3px 9px;
  border-radius: var(--r-pill);
  text-transform: uppercase;
}
.badge-blue {
  background: var(--blue-tint);
  color: var(--blue-deep);
}
.badge-green {
  background: var(--green-tint);
  color: var(--green-deep);
}
.badge-amber {
  background: var(--amber-tint);
  color: var(--amber-deep);
}
.badge-mute {
  background: var(--surface-2);
  color: var(--ink-mute);
}
/* The remaining three of the six hues that have an .eyebrow rule, added so a
   badge can carry a topic label in any of them. The newsletter topic chip keys
   its colour off the TOPIC rather than the item, so two updates about the same
   part of the platform always look alike; emit-newsletters.mjs greps this file
   to prove the hue it was given has a rule here, because a missing one renders
   as unstyled text rather than failing visibly. */
.badge-teal {
  background: var(--teal-tint);
  color: var(--teal-deep);
}
.badge-purple {
  background: var(--purple-tint);
  color: var(--purple-deep);
}
.badge-magenta {
  background: var(--magenta-tint);
  color: var(--magenta-deep);
}

/* ---------------------------------------------------------------------------
 * PRODUCT ICONS — recoloured SVGs (solid spectrum hue baked in), used as
 * background-image (works over file://; no CORS mask issue). url() resolves
 * relative to this CSS file -> assets/icons/.
 * ------------------------------------------------------------------------- */
.ti {
  width: 24px;
  height: 24px;
  flex: none;
  display: inline-block;
  background: var(--ti-src) center / contain no-repeat;
}
.ti.lg {
  width: 34px;
  height: 34px;
}
.ti--empower {
  --ti-src: url(../icons/TC_Empower.svg);
}
.ti--test {
  --ti-src: url(../icons/TC_Test.svg);
}
.ti--automate {
  --ti-src: url(../icons/TC_Automate.svg);
}
.ti--grid {
  --ti-src: url(../icons/TC_Grid.svg);
}
.ti--alm {
  --ti-src: url(../icons/TC_ALM.svg);
}
.ti--api {
  --ti-src: url(../icons/TC_API.svg);
}
.ti--devops {
  --ti-src: url(../icons/TC_Devops.svg);
}
.ti--load {
  --ti-src: url(../icons/TC_Load.svg);
}
.ti--board {
  --ti-src: url(../icons/TC_Board.svg);
}
.ti--analysis {
  --ti-src: url(../icons/TC_Analysis.svg);
}
.ti--finance {
  --ti-src: url(../icons/TC_Finance.svg);
}
.ti--support {
  --ti-src: url(../icons/TC_Support.svg);
}
.ti--okrs {
  --ti-src: url(../icons/TC_OKRs.svg);
}
.ti--product {
  --ti-src: url(../icons/TC_Product.svg);
}

/* tinted square chip behind an icon (spectrum hue) */
.icochip {
  width: 46px;
  height: 46px;
  border-radius: 13px;
  display: grid;
  place-items: center;
  flex: none;
}
.icochip svg {
  width: 24px;
  height: 24px;
  stroke: currentColor;
  fill: none;
}
.icochip--blue {
  background: var(--blue-tint);
  color: var(--blue-deep);
}
.icochip--teal {
  background: var(--teal-tint);
  color: var(--teal-deep);
}
.icochip--green {
  background: var(--green-tint);
  color: var(--green-deep);
}
.icochip--amber {
  background: var(--amber-tint);
  color: var(--amber-deep);
}
.icochip--magenta {
  background: var(--magenta-tint);
  color: var(--magenta-deep);
}
.icochip--red {
  background: var(--red-tint);
  color: var(--red-deep);
}
.icochip--purple {
  background: var(--purple-tint);
  color: var(--purple-deep);
}

/* ---------------------------------------------------------------------------
 * CARDS
 * ------------------------------------------------------------------------- */
.card {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 28px;
  box-shadow: var(--shadow-sm);
  display: flex;
  flex-direction: column;
}
.card .num {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: 15px;
  width: 40px;
  height: 40px;
  border-radius: 12px;
  display: grid;
  place-items: center;
  margin-bottom: 18px;
  background: var(--primary-tint);
  color: var(--primary-deep);
}
.card h3 {
  font-size: 1.375rem;
  margin-bottom: 10px;
}
.card p {
  font-size: var(--fs-sm);
  line-height: 1.55;
  color: var(--ink-mute);
}

.card-hover {
  transition:
    transform var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease);
}
.card-hover:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-bento);
}

/* monday-style colored block, keyed to a hue */
.block {
  border-radius: var(--r-lg);
  padding: 28px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.block h3 {
  font-size: 1.3rem;
}
.block p {
  font-size: var(--fs-sm);
  line-height: 1.55;
}
.block .blocknum {
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 600;
  opacity: 0.85;
}
.block--blue {
  background: var(--blue-tint);
  color: var(--blue-deep);
}
.block--teal {
  background: var(--teal-tint);
  color: var(--teal-deep);
}
.block--green {
  background: var(--green-tint);
  color: var(--green-deep);
}
.block--amber {
  background: var(--amber-tint);
  color: var(--amber-deep);
}
.block--magenta {
  background: var(--magenta-tint);
  color: var(--magenta-deep);
}
.block--purple {
  background: var(--purple-tint);
  color: var(--purple-deep);
}
.block--red {
  background: var(--red-tint);
  color: var(--red-deep);
}
.block p,
.block .blocknum {
  color: var(--ink-soft);
}

.block.solid {
  color: #fff;
}
.block.solid h3,
.block.solid p,
.block.solid .blocknum {
  color: #fff;
}
.block.solid p {
  /* was rgba(255,255,255,.88) = 3.49:1 on the blue gradient */
  color: #fff;
}
.block.solid.block--blue {
  background: var(--grad-blue);
}
.block.solid.block--teal {
  background: linear-gradient(150deg, #49b2b6, #2f8a8e);
}
.block.solid.block--green {
  background: linear-gradient(150deg, #8ac036, #5f8d1d);
}
.block.solid.block--amber {
  background: linear-gradient(150deg, #f6960b, #d97e06);
  color: var(--ink);
}
.block.solid.block--amber h3,
.block.solid.block--amber p {
  color: var(--ink);
}
.block.solid.block--magenta {
  background: linear-gradient(150deg, #d3286e, #a81e57);
}
.block.solid.block--purple {
  background: linear-gradient(150deg, #922791, #6f1c6e);
}

/* feature tile */
.tile {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 24px;
  box-shadow: var(--shadow-sm);
}
.tile h4 {
  font-size: 1.0625rem;
  margin: 14px 0 6px;
}
.tile p {
  font-size: var(--fs-sm);
  line-height: 1.5;
  color: var(--ink-mute);
}

/* Tile with generated art: the art runs edge to edge across the top, so the
   tile drops its own top padding. 15px = --r minus the tile's 1px border. */
.tile--art {
  padding-top: 0;
}
.tile__art {
  margin: 0 -24px var(--s4);
  aspect-ratio: var(--art-ar, 16 / 9);
  background: var(--surface-2);
  /* clipped here rather than on the tile, so an .is-gated review ribbon can
     still hang outside the tile's top edge */
  border-radius: 15px 15px 0 0;
  overflow: hidden;
}
.tile__art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--tile-op, 50% 50%);
  display: block;
}
.tile--art h4 { margin-top: 12px; }

/* Wide art band that sits above a tile row. The source is a 16/9 field with the
   motif in its upper half and the lower third left quiet, so the band crops to
   a strip around the motif. */
.sync-band {
  margin-bottom: var(--s7);
  border-radius: var(--r);
}
.sync-band img { object-position: 50% 34%; }

/* product-module card (featured four) */
.pmcard {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 26px;
  box-shadow: var(--shadow-bento);
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.pmcard .pmtop {
  display: flex;
  align-items: center;
  gap: 12px;
}
.pmcard h3 {
  font-size: 1.375rem;
}
.pmcard .pmtag {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-mute);
}
.pmcard p {
  font-size: var(--fs-sm);
  color: var(--ink-mute);
  line-height: 1.55;
}
.pmcard .pmlink {
  margin-top: auto;
  font-size: var(--fs-sm);
  font-weight: 800;
  color: var(--blue-deep);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.pmcard .pmlink .arw {
  transition: transform var(--dur) var(--ease);
}
.pmcard:hover .pmlink .arw {
  transform: translateX(4px);
}

/* 16-capability grid */
.capgrid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr));
  gap: 12px;
}
.capcell {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  box-shadow: var(--shadow-sm);
  transition:
    transform var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease);
}
.capcell:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-bento);
}
.capcell b {
  font-size: 0.9375rem;
  font-weight: 700;
  color: var(--ink);
}
.capcell small {
  font-size: var(--fs-xs);
  color: var(--ink-mute);
  font-weight: 500;
  line-height: 1.35;
}
.capcell.feat {
  background: var(--blue-tint);
  border-color: transparent;
}
.capcell.feat small {
  color: var(--blue-deep);
}

/* ---------------------------------------------------------------------------
 * STAT / KPI
 * ------------------------------------------------------------------------- */
.stat-card {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 28px 24px;
  box-shadow: var(--shadow-sm);
}
.stat-card .big {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(2.5rem, 4vw, 3.25rem);
  line-height: 1;
  color: var(--primary);
  letter-spacing: -0.03em;
}
.stat-card .lbl {
  margin-top: 10px;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--ink-mute);
}

/* ---------------------------------------------------------------------------
 * TRUST cards + partner wall
 * ------------------------------------------------------------------------- */
.trust {
  display: flex;
  align-items: center;
  gap: 12px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 16px 18px;
  box-shadow: var(--shadow-sm);
}
.trust .tic {
  width: 38px;
  height: 38px;
  border-radius: 10px;
  display: grid;
  place-items: center;
  flex: none;
}
.trust .tic svg {
  width: 20px;
  height: 20px;
  stroke: #fff;
  fill: none;
}
.trust b {
  font-size: 0.9375rem;
  font-weight: 700;
  display: block;
  color: var(--ink);
}
.trust small {
  font-size: var(--fs-xs);
  color: var(--ink-mute);
  font-weight: 500;
}
.tic--green {
  background: linear-gradient(150deg, #8ac036, #5f8d1d);
}
.tic--blue {
  background: var(--grad-blue);
}
.tic--purple {
  background: linear-gradient(150deg, #922791, #6f1c6e);
}
.tic--teal {
  background: linear-gradient(150deg, #49b2b6, #2f8a8e);
}
.tic--amber {
  background: linear-gradient(150deg, #f6960b, #d97e06);
}

.partner-wall {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(24px, 5vw, 56px);
  flex-wrap: wrap;
}
.partner-wall img {
  height: 34px;
  width: auto;
  filter: grayscale(1);
  opacity: 0.62;
  transition:
    filter var(--dur) var(--ease),
    opacity var(--dur) var(--ease);
}
.partner-wall img:hover {
  filter: grayscale(0);
  opacity: 1;
}
.trusted-label {
  font-size: var(--fs-xs);
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-mute);
  text-align: center;
}

/* ---------------------------------------------------------------------------
 * LUMINA — neon badge (the one contained neon exception; Lumina moments only)
 * ------------------------------------------------------------------------- */
.lumina-badge {
  position: relative;
  display: inline-block;
  border-radius: 50px;
  animation: lb-pulse 2.4s ease-in-out infinite alternate;
}
.lumina-badge .lb {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  /* matches .btn-lumina: pink end darkened for the white label */
  background: linear-gradient(135deg, #e6175d, #9c27b0);
  border-radius: 50px;
  border: 2px solid #fff;
  color: #fff;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  height: 30px;
  padding: 0 15px 0 3px;
  overflow: hidden;
  z-index: 2;
}
.lumina-badge .lb::before {
  content: "";
  position: absolute;
  inset: -20px;
  z-index: -1;
  border-radius: 50px;
  background: linear-gradient(
    0deg,
    #390142,
    #52094e,
    #761560,
    #901d6d,
    #a02375,
    #a62578,
    #761560,
    #52094e,
    #390142
  );
  filter: blur(15px);
  opacity: 0.8;
  animation: lb-rotate 3s linear infinite;
}
.lumina-badge .lstar {
  height: 26px;
  width: 26px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  flex: none;
  background: linear-gradient(
    135deg,
    #390142,
    #52094e,
    #761560,
    #901d6d,
    #a02375,
    #a62578
  );
}
.lumina-badge .lstar svg {
  width: 15px;
  height: 15px;
  fill: #fff;
}
@keyframes lb-rotate {
  to {
    transform: rotate(360deg);
  }
}
@keyframes lb-pulse {
  0% {
    box-shadow:
      0 0 5px rgba(233, 30, 99, 0.35),
      0 0 10px rgba(156, 39, 176, 0.35),
      0 0 20px rgba(156, 39, 176, 0.2);
  }
  100% {
    box-shadow:
      0 0 10px rgba(233, 30, 99, 0.7),
      0 0 22px rgba(156, 39, 176, 0.55),
      0 0 44px rgba(156, 39, 176, 0.35);
  }
}
@media (prefers-reduced-motion: reduce) {
  .lumina-badge,
  .lumina-badge .lb::before {
    animation: none;
  }
}

/* Lumina orb (brand touch across the site) */
.lumina-orb {
  position: relative;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  flex: none;
  display: inline-block;
  background: radial-gradient(
    circle at 34% 30%,
    #d9c2ff 0%,
    #a855c8 45%,
    #922791 74%
  );
  box-shadow:
    0 0 0 4px rgba(146, 39, 145, 0.12),
    0 8px 20px -4px rgba(146, 39, 145, 0.45);
}
.lumina-orb .e {
  position: absolute;
  top: 36%;
  width: 15%;
  height: 18%;
  border-radius: 50%;
  background: #fff;
}
.lumina-orb .e.l {
  left: 28%;
}
.lumina-orb .e.r {
  right: 28%;
}

/* Floating contact rail: WhatsApp + back-to-top (rendered once in footer.html). */
/* While the consent banner is up it owns the bottom of the screen: the FAB rail
   sat inside the banner's box at the same z-index, so DOM order alone decided
   that the WhatsApp circle painted over the Accept/Reject row. cookie.js sets
   html.cookie-open while the banner is showing. */
html.cookie-open .floating-contact { display: none; }

.floating-contact {
  position: fixed;
  right: 18px;
  bottom: 18px;
  z-index: 70;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.floating-contact .fab {
  width: 52px;
  height: 52px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  border: 1px solid var(--line);
  background: #fff;
  box-shadow: var(--shadow);
  cursor: pointer;
  transition:
    transform 0.15s var(--ease),
    box-shadow 0.15s var(--ease);
}
.floating-contact .fab:hover {
  transform: translateY(-2px);
  box-shadow: 0 12px 28px rgba(20, 26, 39, 0.18);
}
.floating-contact .fab:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 3px;
}
.floating-contact .fab-wa {
  background: #25d366;
  border-color: #1eb457;
}
.floating-contact .fab-wa svg {
  width: 27px;
  height: 27px;
  fill: #fff;
}
.floating-contact .fab-top svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: var(--ink-soft);
}
.floating-contact .fab-top[hidden] {
  display: none;
}
@media (max-width: 560px) {
  .floating-contact {
    right: 12px;
    bottom: 12px;
  }
  .floating-contact .fab {
    width: 46px;
    height: 46px;
  }
}

.mega-feat {
  display: flex;
  align-items: center;
  gap: 12px;
  background: linear-gradient(100deg, var(--purple-tint), #fff);
  border: 1px solid var(--purple-tint);
  border-radius: 14px;
  padding: 12px 14px;
  margin-bottom: 8px;
}
.mega-feat .lumina-orb {
  width: 34px;
  height: 34px;
}
.mega-feat b {
  font-size: 0.9375rem;
  font-weight: 800;
  color: var(--ink);
  display: block;
}
.mega-feat small {
  font-size: var(--fs-xs);
  color: var(--ink-mute);
  font-weight: 500;
}
.mega-feat .mega-feat-cta {
  margin-left: auto;
  font-size: var(--fs-xs);
  font-weight: 800;
  color: var(--purple-deep);
  flex: none;
}

/* .icochip is 46px on pages; shrink it to sit in the nav rows */
.menu-item .icochip {
  width: 30px;
  height: 30px;
  border-radius: 9px;
}
.menu-item .icochip svg {
  width: 18px;
  height: 18px;
}
.acc-panel-inner a .icochip {
  width: 26px;
  height: 26px;
  border-radius: 8px;
}
.acc-panel-inner a .icochip svg {
  width: 16px;
  height: 16px;
}

/* ---------------------------------------------------------------------------
 * Solutions mega-panel — three BOUNDED persona groups.
 *
 * Each column is a card carrying ONE hue, matched to the eyebrow colour on the
 * page it links to (product = blue, QA = teal, Jira = amber), so the colour
 * carries meaning instead of decorating. The previous version ran the same
 * blue -> purple -> teal -> amber chip sequence down all three columns, which
 * made the eye read horizontal bands ACROSS the columns and fought the very
 * grouping it was meant to support. Removing purple from here also leaves it
 * reserved for Lumina/AI, which the rest of the system already assumes.
 *
 * Reuses .menu-panel.mega + .menu-grid + .menu-item + .icochip (page glyphs).
 * ------------------------------------------------------------------------- */
.menu-panel.mega.menu-sol {
  width: min(960px, calc(100vw - 2 * var(--gutter)));
}
.menu-sol .menu-grid {
  gap: var(--s3);
  align-items: stretch;
}

.sol-col {
  --sol: var(--blue);
  --sol-deep: var(--blue-deep);
  --sol-tint: var(--blue-tint);
  display: flex;
  flex-direction: column;
  position: relative;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--line);
  /* Square top so the persona accent bar reads as a clean full-width rule;
     only the bottom is rounded. */
  border-radius: 0 0 var(--r) var(--r);
  padding: 3px 6px 10px;
}
.sol-col--qa {
  --sol: var(--teal);
  --sol-deep: var(--teal-deep);
  --sol-tint: var(--teal-tint);
}
.sol-col--jira {
  --sol: var(--amber);
  --sol-deep: var(--amber-deep);
  --sol-tint: var(--amber-tint);
}
.sol-col::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 3px;
  background: var(--sol);
}

.sol-col__head {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 10px 3px 8px;
  padding: 9px 8px 12px;
  border-bottom: 1px solid var(--line);
  border-radius: 10px;
  transition: background var(--dur-fast) var(--ease);
}
.sol-col__head:hover {
  background: var(--sol-tint);
}
.sol-col__head .icochip {
  width: 34px;
  height: 34px;
  border-radius: 10px;
  background: var(--sol-tint);
  color: var(--sol-deep);
}
.sol-col__head .icochip svg {
  width: 20px;
  height: 20px;
}
/* Fixed two-line box: keeps the three head rules on one baseline whatever
   the persona line says. Without it a wrap in one column drops its divider
   below the other two and the rows stop reading as parallel groups. */
.sol-col__head b {
  display: block;
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--ink);
  line-height: 1.2;
  margin-top: 2px;
  min-height: 2.4em;
}
.sol-col__kicker {
  display: block;
  font-size: 0.6875rem;
  font-weight: 800;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--sol-deep);
}
.sol-col__arrow {
  margin-left: auto;
  color: var(--sol-deep);
  font-weight: 800;
  opacity: 0;
  transform: translateX(-3px);
  transition:
    opacity var(--dur-fast) var(--ease),
    transform var(--dur-fast) var(--ease);
}
.sol-col__head:hover .sol-col__arrow,
.sol-col__head:focus-visible .sol-col__arrow {
  opacity: 1;
  transform: none;
}

.sol-col .menu-item {
  padding: 8px;
  border-radius: 10px;
}
.sol-col .menu-item:hover {
  background: var(--sol-tint);
}
.sol-col .menu-item .icochip {
  width: 26px;
  height: 26px;
  border-radius: 8px;
  background: var(--sol-tint);
  color: var(--sol-deep);
}
.sol-col .menu-item .icochip svg {
  width: 15px;
  height: 15px;
}

/* Current row inside a persona card: stay in the column's hue rather than
   dropping to the generic blue, so the grouping still reads. */
.sol-col .menu-item.is-current { background: var(--sol-tint); }
.sol-col .menu-item.is-current b { color: var(--sol-deep); }
.sol-col__head.is-current { background: var(--sol-tint); }
.acc-group > a.is-current { background: var(--sol-tint) !important; color: var(--sol-deep) !important; }
/* Lumina card in the Platform panel, when you are on lumina.html */
.mega-feat.is-current { border-color: var(--purple); box-shadow: inset 0 0 0 1px var(--purple); }

/* Mark the group the visitor is already inside (assets/js/nav.js). */
.sol-col.is-current {
  border-color: var(--sol);
  box-shadow: inset 0 0 0 1px var(--sol);
}

/* Columns settle in sequence so the grouping is legible as the panel opens. */
.menu-sol .sol-col {
  opacity: 0;
  transform: translateY(6px);
  transition:
    opacity var(--dur) var(--ease),
    transform var(--dur) var(--ease);
}
.has-menu.is-open .menu-sol .sol-col {
  opacity: 1;
  transform: none;
}
.menu-sol .sol-col:nth-child(2) {
  transition-delay: 45ms;
}
.menu-sol .sol-col:nth-child(3) {
  transition-delay: 90ms;
}

/* Tactile press feedback — absent on every menu row until now. */
.menu-item:active,
.sol-col__head:active,
.menu-all:active {
  transform: translateY(1px);
}

/* ---- Drawer counterpart: same three groups, same hues -------------------- */
/* A full card border is too heavy in a 400px drawer, so the group reads from a
   left rule plus the same head treatment as the desktop card. */
.acc-group {
  --sol: var(--blue);
  --sol-deep: var(--blue-deep);
  --sol-tint: var(--blue-tint);
  display: flex;
  flex-direction: column;
  gap: 2px;
  border-left: 3px solid var(--sol);
  border-radius: 3px;
  padding-left: 10px;
  margin: 14px 0 6px;
}
.acc-group--qa {
  --sol: var(--teal);
  --sol-deep: var(--teal-deep);
  --sol-tint: var(--teal-tint);
}
.acc-group--jira {
  --sol: var(--amber);
  --sol-deep: var(--amber-deep);
  --sol-tint: var(--amber-tint);
}
.acc-panel-inner .acc-group__head {
  align-items: flex-start;
  gap: 10px;
  padding: 8px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--line);
  border-radius: 10px;
}
.acc-panel-inner .acc-group__head:hover {
  background: var(--sol-tint);
}
.acc-panel-inner .acc-group__head .icochip {
  width: 32px;
  height: 32px;
  border-radius: 10px;
  background: var(--sol-tint);
  color: var(--sol-deep);
}
.acc-panel-inner .acc-group__head .icochip svg {
  width: 19px;
  height: 19px;
}
.acc-panel-inner .acc-group__head b {
  display: block;
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--ink);
  line-height: 1.2;
  margin-top: 2px;
}
.acc-group .acc-group__head .sol-col__arrow {
  opacity: 1;
  transform: none;
}
.acc-group > a:not(.acc-group__head) .icochip {
  background: var(--sol-tint);
  color: var(--sol-deep);
}
.acc-group > a:not(.acc-group__head):hover {
  background: var(--sol-tint);
}

@media (prefers-reduced-motion: reduce) {
  .menu-sol .sol-col {
    opacity: 1;
    transform: none;
    transition: none;
  }
  .sol-col__arrow {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* Lumina wordmark — the word "Lumina" carried through body copy in the Lumina
   brand gradient so it is easy to spot wherever it is mentioned. Wrapped by
   assets/js/luminaword.js (skips the neon pill, buttons and the typing prompt). */
.lumina-word {
  font-weight: 700;
  color: #a21caf;
  background: linear-gradient(120deg, #9333ea, #c026d3);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}
/* On dark sections and the footer, use a lighter magenta for contrast. */
.section.ink .lumina-word,
.finale .lumina-word,
.site-footer .lumina-word {
  background: linear-gradient(120deg, #d456ef, #e879f9);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: #e879f9;
}

/* ---------------------------------------------------------------------------
 * MEDIA FRAMES — browser / device / video / drop-zone / generic aspect frame
 * ------------------------------------------------------------------------- */
.media-frame {
  position: relative;
  width: 100%;
  border-radius: var(--r-lg);
  overflow: hidden;
}
.media-frame[data-aspect] {
  aspect-ratio: var(--ar, 16 / 9);
}
.media-frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.browser {
  width: 100%;
  border-radius: 16px;
  overflow: hidden;
  background: #fff;
  box-shadow: var(--shadow-frame);
  border: 1px solid var(--line);
}
.browser__bar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 15px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--line);
}
.browser__dots {
  display: flex;
  gap: 6px;
}
.browser__dots i {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  display: block;
}
.browser__dots i:nth-child(1) {
  background: #ff5f57;
}
.browser__dots i:nth-child(2) {
  background: #febc2e;
}
.browser__dots i:nth-child(3) {
  background: #28c840;
}
.browser__url {
  flex: 1;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 7px 12px;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-mute);
  display: flex;
  align-items: center;
  gap: 7px;
}
.browser__url .lock {
  width: 12px;
  height: 12px;
  stroke: var(--green-deep);
  fill: none;
  flex: none;
}
.browser__body {
  position: relative;
  background: var(--surface);
  overflow: hidden;
}
.browser__body svg,
.browser__body img {
  display: block;
  width: 100%;
  height: auto;
}

/* ---------------------------------------------------------------------------
 * LIGHTBOX — click-to-expand for [data-zoomable] visuals (assets/js/lightbox.js)
 * ------------------------------------------------------------------------- */
.lightbox {
  position: fixed;
  inset: 0;
  /* Above the skip link (200), which it previously tied with and beat only by
     being appended to <body> last. */
  z-index: 300;
  display: grid;
  place-items: center;
  padding: var(--gutter);
  background: rgba(14, 21, 37, 0.82);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
.lightbox[hidden] {
  display: none;
}
.lightbox__img {
  max-width: min(1200px, 96vw);
  max-height: 90vh;
  width: auto;
  height: auto;
  border-radius: var(--r);
  background: #fff;
  box-shadow: var(--shadow-frame);
}
.lightbox__close {
  position: absolute;
  top: var(--s5);
  right: var(--s5);
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: #fff;
  color: var(--ink);
  border: none;
  cursor: pointer;
  box-shadow: var(--shadow);
}
.lightbox__close:hover {
  background: var(--surface-2);
}

.videoframe {
  position: relative;
  border-radius: var(--r-lg);
  overflow: hidden;
  border: 1px solid var(--line);
  box-shadow: var(--shadow-bento);
  background: var(--grad-deep);
  aspect-ratio: 16/9;
  display: grid;
  place-items: center;
}
/* Any img, not just img.poster: the demo page puts a bare <img> in a
   .videoframe, which therefore had no sizing rule at all. While the image
   loaded it was fine, but on a failed load it fell back to its 1920px width
   attribute and ran up to 1547px past the viewport. */
.videoframe video,
.videoframe img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.videoframe .play {
  position: relative;
  z-index: 2;
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.94);
  display: grid;
  place-items: center;
  box-shadow: var(--shadow);
  border: none;
  cursor: pointer;
  transition: transform var(--dur) var(--ease-pop);
}
.videoframe .play:hover {
  transform: scale(1.06);
}
.videoframe .play svg {
  width: 24px;
  height: 24px;
  fill: var(--blue-deep);
  margin-left: 3px;
}
.videoframe .vlbl {
  position: absolute;
  z-index: 2;
  bottom: 14px;
  left: 16px;
  font-size: var(--fs-xs);
  font-weight: 800;
  color: #fff;
  letter-spacing: 0.04em;
}

/* ---------------------------------------------------------------------------
 * STORY FRAME — a self-contained interactive shown inside a normal page.
 *
 * Why a frame and not injected markup: each story file ships its own :root
 * token block redefining names in the same family as tokens.css, plus
 * html,body{margin:0}. Injecting it would be a token collision by
 * construction. The frame is the CSS boundary, in both directions, for free.
 *
 * The story's own shell is position:fixed;inset:0, which resolves against the
 * FRAME's viewport, not the page's. So giving the frame a size is all it needs
 * and nothing inside the file changes.
 * ------------------------------------------------------------------------- */
.storyframe {
  position: relative;
  width: 100%;
  /* The one number the box is derived from: the room under the header, less the
     control bar below the frame. svh, not vh: on mobile Safari 100vh counts the
     retracting toolbar, which pushes the story's own nav row and progress bar
     below the fold — and those are the only visible way to advance besides the
     swipe. */
  --frame-room: calc(100svh - var(--header-h) - 96px);
  /* Height, not a ratio. The story's shell is a grid of auto 1fr auto and it
     spends a CONSTANT 125px of whatever box it is given on its own top bar and
     nav row, at every width — so what it needs is vertical room, not a shape.
     A 16/9 box left the content a 295px stage. 640px is the measured point at
     which a heading, its lead and the first card are all visible; 720px is
     where most screens stop scrolling internally.
     One min() also means there is no min/max pair left to invert. The old
     max-height / min-height: 420px pair could not both hold, and CSS applies
     min-height LAST, so the max was discarded outright rather than split —
     420px of frame in the 322px a landscape phone has under the header. */
  height: min(720px, var(--frame-room));
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--surface);
  box-shadow: var(--shadow-bento);
  contain: layout paint;
}
@supports not (height: 100svh) {
  .storyframe { --frame-room: calc(100vh - var(--header-h) - 96px); }
}
.storyframe__frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}
/* Shows where the keys are going. The story's own controls are focusable, so
   this fires as soon as the visitor clicks or tabs in. */
.storyframe:focus-within {
  border-color: var(--blue);
  box-shadow: var(--shadow-bento), 0 0 0 3px rgba(24, 104, 219, 0.18);
}
/* ---------------------------------------------------------------------------
 * THE VEIL — shown when the screen cannot fit a whole document.
 *
 * MEASURED CAUSE, because it is not the obvious one. Each document's shell is
 * .deck{display:grid;grid-template-rows:auto 1fr auto;overflow:hidden}. Rows
 * are declared, so the single COLUMN is `auto` and is widened to the widest
 * row's max-content — which is the nav row, whose width is set by its dot
 * count. The stage inherits that width and .deck's overflow:hidden then clips
 * the excess, with no scrollbar and no cue. Measured in the frame at 390px:
 *
 *   getting-started / plans-and-pricing   7 dots   nav 430px    40px lost
 *   jira-two-way-sync                    11 dots   nav 494px   104px lost
 *   import-test-cases                    13 dots   nav 526px   136px lost
 *
 * At 844px all four are clean. So the fix is width, and the reader has to get
 * it somewhere the page cannot give it. The floors: 600px wide (the 526px worst
 * case plus slack, since fitting exactly leaves the nav row no room to
 * breathe), and 420px tall before the stage clears 300px. A phone has one or
 * the other and never both, so the way out is a new tab, where the document
 * gets the whole screen with no page header and no control bar below it.
 *
 * absolute, not fixed: .storyframe sets contain:layout paint, which makes it
 * the containing block for a fixed descendant — the same trap documented on
 * .menu-panel.mega in layout.css. Being inside the frame also keeps its
 * z-index local, so it needs no place in the page ladder.
 * ------------------------------------------------------------------------- */
.storyframe__veil {
  display: none;
  position: absolute;
  inset: 0;
  z-index: 2;
  place-items: center;
  padding: var(--gutter);
  /* The drawer's scrim (.42), not the lightbox's blackout (.82): the reader is
     being pointed somewhere, not told something failed, so the document should
     still read through. rgba(14,21,37,a) is the site's scrim colour. */
  background: rgba(14, 21, 37, 0.42);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
/* .cookie-banner's surface — the one shipped card built to sit over content. */
.veil__card {
  max-width: 34ch;
  text-align: center;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r);
  box-shadow: var(--shadow-frame);
  padding: var(--s5);
  /* The card lives inside the frame's overflow:hidden, so it must never be
     taller than the box it is centred in — otherwise the very short frames that
     trigger it are the ones that cut its buttons off. */
  max-height: 100%;
  overflow-y: auto;
}
.veil__ic {
  display: inline-grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 12px;
  margin-bottom: var(--s3);
  background: var(--blue-tint);
}
.veil__ic svg { width: 24px; height: 24px; stroke: var(--blue-deep); fill: none; }
.veil__head { font-weight: 800; color: var(--ink); line-height: 1.3; margin-bottom: var(--s2); }
.veil__body {
  font-size: var(--fs-sm);
  color: var(--ink-mute);
  line-height: 1.45;
  margin-bottom: var(--s5);
}
/* Stacked, not a row: at 288px the two labels do not fit side by side, and the
   recommended action should be the wider target. */
.veil__acts { display: flex; flex-direction: column; gap: var(--s2); }

/* Which sentence shows. Default is the one that is true everywhere. */
.veil__rotate { display: none; }
.veil__plain { display: block; }

/* The trigger. Two conditions, comma-separated — an OR.
   639.98px is where the frame passes 600px wide, and is the value layout.css
   already uses for this same edge. 580px is where it passes 420px tall, and is
   the site's first height-keyed breakpoint: every other one here is width,
   pointer, motion or print, because height is normally handled in calc() with
   svh. That does not work when the answer is "cover the frame" rather than
   "resize it". */
@media (max-width: 639.98px), (max-height: 580px) {
  .storyframe__veil:not([hidden]) { display: grid; }
}
/* Say "turn your phone" only where turning it buys width, on something that can
   be turned. pointer:coarse is used everywhere else on this site purely to
   raise a hit area to 44px; this is the one place it gates content, because a
   500x900 desktop window is orientation:portrait too and must not be told it is
   a phone. */
@media (max-width: 639.98px) and (orientation: portrait) and (pointer: coarse) {
  .veil__rotate { display: block; }
  .veil__plain { display: none; }
}
/* A short frame cannot hold the tall card. At 844x390 the frame is 226px and the
   stacked card wants ~260px, so it was clipped exactly where it is needed most.
   Compact it: the icon is decoration, and any viewport short enough to land here
   has width to spare, so the actions go in a row. */
@media (max-height: 580px) {
  .veil__card { max-width: 56ch; padding: var(--s4); }
  .veil__ic { display: none; }
  .veil__head { margin-bottom: var(--s1); }
  .veil__body { margin-bottom: var(--s4); }
  .veil__acts { flex-direction: row; flex-wrap: wrap; justify-content: center; }
}
/* No JS, no dismiss. Everything else on the card still works, and for the three
   panels whose iframe never receives a src it is the only useful thing there. */
html:not(.js) [data-story-unveil] { display: none; }

.storyframe__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s4);
  margin-top: var(--s5);
}
.storyframe__hint { margin: 0; color: var(--ink-mute); font-size: var(--fs-sm); }
.storyframe__hint kbd {
  font-family: var(--font-mono);
  font-size: 0.85em;
  padding: 1px 5px;
  border: 1px solid var(--line);
  border-bottom-width: 2px;
  border-radius: 5px;
  background: var(--surface-2);
}
.storyframe__acts { display: flex; flex-wrap: wrap; gap: var(--s3); margin-left: auto; }
/* "Select it, then use the arrow keys" is instructions for hardware a phone does
   not have. Back and Next stay: they proxy real clicks on the document's own
   buttons, so they work under a finger. */
@media (pointer: coarse) {
  .storyframe__hint { display: none; }
}
/* A frame cannot paginate. The story's own print button prints its document one
   screen per page, so route readers there rather than printing a torn frame. */
@media print { .storyframe, .storyframe__acts { display: none; } }

/* ---------------------------------------------------------------------------
 * PDF FRAME — a published newsletter PDF shown inside a normal page.
 * Sibling of .storyframe above, and it reuses that block's .veil__* classes
 * verbatim, which is why it lives here rather than in sections.css: splitting
 * would put the veil's two halves in two files.
 *
 * <iframe>, not <object> or <embed>. <object type="application/pdf"> looks
 * right because content between its tags is a declared fallback — but the
 * fallback fires on LOAD failure, not RENDER failure. A browser that downloads
 * the PDF instead of rendering it has loaded the resource successfully, so the
 * fallback never appears and the reader gets a blank box. The fallback
 * therefore has to be a visible sibling that CSS decides on, which is exactly
 * what the veil below is. <embed> has no title, so it announces as an
 * unlabelled embed, and no loading="lazy".
 *
 * Sized on .storyframe's rule: the ONE number the box derives from is the room
 * under the header less the control bar, in svh not vh, with exactly one min()
 * so there is no min/max pair left to invert (CSS applies min-height LAST).
 *   940 not 720: a PDF viewer spends a constant ~56px on its own toolbar, and
 *     an A4 page at a readable zoom needs the rest. At 720 total the page
 *     renders near 63% and body text falls under 8px.
 *   120 not 96: .pdfframe__bar carries three buttons rather than two and a
 *     hint, so it wraps to two rows below 900px.
 * ------------------------------------------------------------------------- */
.pdfframe {
  position: relative;
  width: 100%;
  --frame-room: calc(100svh - var(--header-h) - 120px);
  height: min(940px, var(--frame-room));
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--surface-2);
  box-shadow: var(--shadow-bento);
  /* Makes this the containing block for a position:fixed descendant, which is
     why the veil is absolute — the trap documented on .storyframe__veil and on
     .menu-panel.mega in layout.css. */
  contain: layout paint;
}
@supports not (height: 100svh) {
  .pdfframe { --frame-room: calc(100vh - var(--header-h) - 120px); }
}
.pdfframe__frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
  background: var(--surface-2);
}
/* Shows where the keys are going, once the reader clicks or tabs into the
   viewer's own controls. Same affordance as .storyframe:focus-within. */
.pdfframe:focus-within {
  border-color: var(--blue);
  box-shadow: var(--shadow-bento), 0 0 0 3px rgba(24, 104, 219, 0.18);
}

/* ---- The PDF veil ---------------------------------------------------------
 * MEASURED CAUSE, and it is NOT the same one as the storyframe's.
 *
 * iOS Safari and Android Chrome do not render a PDF in an iframe at all: they
 * paint page one as a static, unscrollable, unzoomable thumbnail. There is no
 * viewport at which that becomes usable, so there is nothing to shrink into —
 * which is why the third trigger is pointer/hover and not a size at all.
 * Opening the file instead hands the reader the OS viewer, with pinch-zoom,
 * text search, page thumbnails, share and save. An iframe offers none of those,
 * so on a phone the new tab is the better product, not a compromise.
 *
 * On a desktop-class engine the floors are width and height. A4 portrait is
 * 794 CSS px at 100%; Chrome's viewer fits-to-width and adds its toolbar, so
 * below ~600px of frame width 10pt body text renders under 8px, and below
 * ~520px of frame height the toolbar and page shadow leave less than one
 * screenful. 639.98px and 580px are reused rather than invented: they are the
 * values .storyframe__veil already uses for these same two edges.
 * ------------------------------------------------------------------------- */
.pdfframe__veil {
  display: none;
  position: absolute;
  inset: 0;
  z-index: 2;
  place-items: center;
  padding: var(--gutter);
  background: rgba(14, 21, 37, 0.42);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
@media (max-width: 639.98px), (max-height: 580px), (pointer: coarse) and (hover: none) {
  .pdfframe__veil:not([hidden]) { display: grid; }
}
/* "Show it here anyway" is a lie on a touch device: there is nothing behind the
   veil to show, because the engine will not render it. Offer only the two
   things that work. This is the one place this page gates content on pointer
   type, the same exception .storyframe__veil takes above. */
@media (pointer: coarse) and (hover: none) {
  [data-news-unveil] { display: none; }
}
/* Say "turn your phone" only where turning it buys width, on something that can
   be turned — a 500x900 desktop window is orientation:portrait too. */
@media (max-width: 639.98px) and (orientation: portrait) and (pointer: coarse) {
  .pdfframe__veil .veil__rotate { display: block; }
  .pdfframe__veil .veil__plain { display: none; }
}
/* The browser cannot render a PDF inline at all (navigator.pdfViewerEnabled is
   false — PDF viewing disabled, or a handler set to download). Nothing will
   ever appear in the frame, at any size, so the veil is not a size question
   here: force it up and drop the dismiss, which cannot help. newsletters.js
   sets the class. */
.pdfframe.is-noviewer .pdfframe__veil:not([hidden]) { display: grid; }
.pdfframe.is-noviewer [data-news-unveil] { display: none; }

/* No JS, no dismiss. The card's two links still work, and with JS off the frame
   already carries the newest issue's real src. */
html:not(.js) [data-news-unveil] { display: none; }

.pdfframe__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s4);
  margin-top: var(--s5);
}
.pdfframe__hint { margin: 0; color: var(--ink-mute); font-size: var(--fs-sm); }
.pdfframe__hint kbd {
  font-family: var(--font-mono);
  font-size: 0.85em;
  padding: 1px 5px;
  border: 1px solid var(--line);
  border-bottom-width: 2px;
  border-radius: 5px;
  background: var(--surface-2);
}
.pdfframe__acts { display: flex; flex-wrap: wrap; gap: var(--s3); margin-left: auto; }
/* Arrow-key instructions are for hardware a phone does not have, and "Read it
   here" focuses a frame a touch engine will not render. */
@media (pointer: coarse) {
  .pdfframe__hint { display: none; }
  [data-news-focus] { display: none; }
}
/* A frame cannot paginate, and printing a PDF viewer prints a screenshot of
   page one. Route the reader to the archive, which pages.css prints with
   resolved URLs. Same reasoning as the .storyframe print rule above. */
@media print { .pdfframe, .pdfframe__bar { display: none; } }

/* drop-zone (labelled capture placeholder) */
.dz {
  position: relative;
  border: 2px dashed var(--line);
  border-radius: var(--r);
  background: repeating-linear-gradient(
    45deg,
    var(--surface-2),
    var(--surface-2) 12px,
    #fff 12px,
    #fff 24px
  );
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  text-align: center;
  padding: 24px;
  color: var(--ink-mute);
  min-height: 160px;
}
.dz[data-aspect] {
  aspect-ratio: var(--ar, 16 / 9);
}
.dz .dzic {
  width: 34px;
  height: 34px;
  stroke: var(--ink-faint);
  fill: none;
}
.dz b {
  font-size: var(--fs-sm);
  font-weight: 800;
  color: var(--ink-soft);
}
.dz small {
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--ink-mute);
  max-width: 34ch;
  line-height: 1.45;
}
.dz .dztag {
  position: absolute;
  top: 10px;
  right: 10px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  background: var(--ink);
  color: #fff;
  padding: 3px 8px;
  border-radius: 100px;
  letter-spacing: 0.04em;
}

/* skeleton shimmer (live-widget loading state) */
.skel {
  background: linear-gradient(
    100deg,
    var(--surface-2) 30%,
    var(--surface-3) 50%,
    var(--surface-2) 70%
  );
  background-size: 200% 100%;
  border-radius: 8px;
  animation: shimmer 1.6s linear infinite;
}
@keyframes shimmer {
  from {
    background-position: 200% 0;
  }
  to {
    background-position: -200% 0;
  }
}

/* ---------------------------------------------------------------------------
 * FORMS
 * ------------------------------------------------------------------------- */
.formcard {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 28px;
  box-shadow: var(--shadow-bento);
}
.formcard h3 {
  font-size: 1.375rem;
  margin-bottom: 6px;
}
.formcard .form-intro {
  font-size: var(--fs-sm);
  color: var(--ink-mute);
  margin-bottom: var(--s6);
}
.field {
  margin-bottom: var(--s4);
}
.field label {
  display: block;
  font-size: var(--fs-xs);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-mute);
  margin-bottom: 6px;
}
/* --red is 3.92:1 on white; --red-deep is 5.87:1. The required-field marker
   carries meaning, so it has to be readable. */
.field label .req {
  color: var(--red-deep);
}
.field .inp,
.field textarea,
.field select {
  width: 100%;
  border: 1.5px solid var(--line);
  border-radius: 12px;
  padding: 12px 14px;
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--ink);
  background: #fff;
  transition:
    border-color var(--dur-fast) var(--ease),
    box-shadow var(--dur-fast) var(--ease);
}
.field .inp:focus,
.field textarea:focus,
.field select:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px var(--blue-tint);
}
.field textarea {
  min-height: 110px;
  resize: vertical;
}
/* iOS Safari zooms the viewport when a focused control is under 16px, and the
   page (correctly) sets no maximum-scale to stop it. --fs-sm is 15px, so every
   field on the demo form triggered it. Only touch needs the bump. */
@media (pointer: coarse) {
  .field .inp,
  .field textarea,
  .field select { font-size: 16px; }
}
.field .err-msg {
  display: none;
  margin-top: 6px;
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--red-deep);
}
.field.has-error .inp,
.field.has-error textarea,
.field.has-error select {
  border-color: var(--red);
}
.field.has-error .err-msg {
  display: block;
}
.form-hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}
.form-note {
  font-size: var(--fs-xs);
  color: var(--ink-mute);
  margin-top: var(--s3);
}
.form-success {
  display: none;
  text-align: center;
  padding: var(--s6) 0;
}
.form-success.is-visible {
  display: block;
}
.form-success .check {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--green-tint);
  display: grid;
  place-items: center;
  margin: 0 auto var(--s4);
}
.form-success .check svg {
  width: 28px;
  height: 28px;
  stroke: var(--green-deep);
  fill: none;
}

/* ---------------------------------------------------------------------------
 * COOKIE banner
 * ------------------------------------------------------------------------- */
.cookie-banner {
  position: fixed;
  left: 16px;
  right: 16px;
  bottom: 16px;
  /* Above the FAB rail (70) so the order is stated, not inherited from DOM order. */
  z-index: 80;
  max-width: 760px;
  margin-inline: auto;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r);
  box-shadow: var(--shadow-frame);
  padding: 16px 18px;
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}
.cookie-banner p {
  flex: 1;
  min-width: 220px;
  font-size: var(--fs-sm);
  color: var(--ink-soft);
  font-weight: 500;
  line-height: 1.45;
  margin: 0;
}
.cookie-banner p a {
  font-weight: 700;
}
.cookie-actions {
  display: flex;
  gap: 8px;
  flex: none;
}

/* ---------------------------------------------------------------------------
 * PAGE FEEDBACK — "Was this page useful?" strip in the footer. Quiet by
 * design: it sits under the footer band, reads as chrome, and collapses to a
 * one-line thank-you once answered. Mechanism: assets/js/feedback.js.
 * ------------------------------------------------------------------------- */
.pagefeedback {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 12px;
  padding: 18px 20px 4px;
  text-align: center;
}
.pagefeedback-q,
.pagefeedback-thanks {
  margin: 0;
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--ink-soft);
}
.pagefeedback-actions {
  display: flex;
  gap: 8px;
  flex: none;
}
/* Narrower than the default btn-sm: this is an aside, not a call to action. */
.pagefeedback-actions .btn {
  min-width: 64px;
}
.pagefeedback-thanks a {
  font-weight: 700;
}
/* A "no" is the answer worth following up, so the ask carries brand colour. */
.pagefeedback.is-negative .pagefeedback-thanks a {
  color: var(--primary);
  text-decoration: underline;
  text-underline-offset: 2px;
}
@media (max-width: 520px) {
  .pagefeedback {
    padding: 14px 16px 2px;
  }
}

/* ---------------------------------------------------------------------------
 * HONEYCOMB — brand accent. The brand hues held in discrete, tessellated hex
 * cells (per brand review: colours contained in a honeycomb, never blended
 * into a rainbow). Reused by .finale .band-art (sections.css). Token: --honeycomb.
 * ------------------------------------------------------------------------- */
.honeycomb {
  display: block;
  width: 120px;
  height: 33px;
  background: var(--honeycomb) center / contain no-repeat;
}

/* ---------------------------------------------------------------------------
 * CLAIM GATE — presentation (mechanism set by inline <head> snippet + gate.js)
 * ------------------------------------------------------------------------- */
/* internal-review build: gated blocks render with a corner review ribbon */
.is-gated {
  position: relative;
  outline: 1.5px dashed rgba(233, 168, 0, 0.55);
  outline-offset: 3px;
  border-radius: 6px;
}
.gate-ribbon {
  position: absolute;
  top: -11px;
  left: 12px;
  z-index: 8;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.02em;
  background: var(--gate-ribbon-bg);
  color: var(--gate-ribbon-ink);
  padding: 3px 9px;
  border-radius: 100px;
  box-shadow: var(--shadow-sm);
  pointer-events: none;
  text-transform: none;
}
.gate-ribbon.is-confirmed {
  background: var(--green-tint);
  color: var(--green-deep);
}
/* subtle marker for small / inline gated items (no ribbon) */
.gate-inline {
  border-bottom: 1px dashed rgba(233, 168, 0, 0.85);
}
.gate-inline.is-confirmed {
  border-bottom-color: rgba(95, 141, 29, 0.6);
}

/* public build: hide any block whose category is not yet confirmed */
html[data-claims="public"][data-gate-lumina="off"] [data-gate~="lumina"],
html[data-claims="public"][data-gate-platform="off"] [data-gate~="platform"],
html[data-claims="public"][data-gate-security="off"] [data-gate~="security"],
html[data-claims="public"][data-gate-integrations="off"]
  [data-gate~="integrations"],
html[data-claims="public"][data-gate-clients="off"] [data-gate~="clients"] {
  display: none !important;
}
