:root {
  --cap-ink: #18211d;
  --cap-paper: #f2f0e9;
  --cap-accent: #d8ff45;
  --cap-muted: #86918b;
  --cap-white: #fff;
  --cap-charcoal: #27302c;
  --cap-table-line: #c9cec7;
  --cap-table-header: #eef1ea;
}

* { box-sizing: border-box; }
/* The canvas. Its width is set per breakpoint by the generated stylesheet (export) or by
   the editor stage; the surround shows the page background. Overflow stays visible so a
   layer set to extend can run out over that background. */
.cap-page { width: 100%; min-width: 0; margin-inline: auto; color: var(--cap-ink); background: var(--cap-paper); font-family: var(--cap-font-body); line-height: 1.5; overflow: visible; container-type: inline-size; }
/* Optional "floating card" presentation, driven by the canvas's pageRadius / pageShadow
   (see `pageCard`). Clipping is what makes the rounded corners actually cut the content;
   it is only on in card mode, so a layer set to extend over the background still overhangs
   normally on a full-bleed page. */
.cap-page--card { border-radius: var(--cap-page-radius, 0); box-shadow: var(--cap-page-shadow, none); overflow: clip; }
.cap-page h1, .cap-page h2, .cap-page h3, .cap-page p, .cap-page figure { margin: 0; }
.cap-page h1, .cap-page h2, .cap-page h3 { font-family: var(--cap-font-heading); font-weight: 500; letter-spacing: -.045em; line-height: .95; }
.cap-page h1 { font-size: clamp(4rem, 10vw, 9.5rem); max-width: 10ch; }
.cap-page h2 { font-size: clamp(3.2rem, 7vw, 7rem); max-width: 10ch; }
.cap-page h3 { font-size: clamp(2rem, 3vw, 3rem); }
.cap-section-frame { position: relative; isolation: isolate; }
.cap-section-frame[data-fixed-width="true"] { flex: none; max-width: none; }
.cap-section-frame > section { position: relative; z-index: 1; }
.cap-section-frame[data-fixed-height="true"] > section { width: 100%; height: 100%; min-height: 0 !important; max-height: 100%; overflow: hidden; }
.cap-section-frame[data-has-background="true"] > section { background: transparent !important; }
.cap-section-frame[data-has-background="true"] .cap-hero__media { display: none; }
.cap-section-background { position: absolute; inset: 0; z-index: 0; overflow: hidden; pointer-events: none; }
/* Full-bleed: break the background out past the canvas's own authored width to the true browser
   edge on a viewport wider than it. `.cap-page`'s `zoom:min(1, 100vw/canvasWidth)` re-scales a
   painted vw-based length by that same zoom factor, so a plain `100vw` here would paint short on
   a narrow (zoomed-down) viewport; `max(100vw, var(--cap-canvas-width))` paints correctly in both
   regimes — see AGENTS.md's Phase 2 note for the derivation. Only left/right/width/transform are
   set; top/bottom stay 0 from the shorthand above, so this is a purely horizontal change. */
.cap-section-background.cap-section-background--full-bleed {
  left: 50%;
  right: auto;
  width: max(100vw, var(--cap-canvas-width, 100%));
  transform: translateX(-50%);
}
/* Parallax that is allowed to leave its block.
   A picture only has as much room to drift as the slack inside its own box — a hero has 8%
   to spare, a plain image layer has none — so past that it has to be let out or it just
   slides and shows an edge. Every box between the moving element and the page is unclipped,
   which is what lets it carry on into the block above or below. */
[data-parallax-overflow="true"],
[data-parallax-overflow="true"] > section,
[data-parallax-overflow="true"] .cap-hero,
[data-parallax-overflow="true"] .cap-section-background,
[data-parallax-overflow="true"] .cap-full-image,
[data-parallax-overflow="true"] > .cap-interactive,
[data-parallax-overflow="true"].cap-layer-image > .cap-interactive { overflow: visible; }
/* Behind its neighbours rather than over them: a picture drifting past its own block should
   read as depth, not as something escaping. */
[data-parallax-overflow="true"] { z-index: 0; }
.cap-section-background__media { width: 100%; height: 100%; display: block; object-fit: cover; transform-origin: center; }
.cap-section-frame.is-selected::after { content: ""; position: absolute; inset: 0; z-index: 30; border: 3px solid #8fff35; pointer-events: none; box-shadow: inset 0 0 0 1px rgba(0,0,0,.4); }
.cap-section-frame[data-pin="true"] > section { position: relative; }
.cap-dark { color: var(--cap-white); background: var(--cap-ink); }
.cap-bound { width: min(100% - clamp(2rem, 6vw, 7rem), 1500px); margin-inline: auto; }
.cap-pad { padding-block: clamp(5rem, 12vw, 11rem); }
.cap-eyebrow { display: inline-block; font-family: var(--cap-font-small-heading, var(--cap-font-body)); font-size: .72rem; line-height: 1.1; font-weight: 700; letter-spacing: .15em; text-transform: uppercase; }
.cap-section-heading { display: grid; grid-template-columns: minmax(140px, 1fr) 3fr; gap: 3rem; align-items: start; margin-bottom: clamp(4rem, 8vw, 8rem); }
.cap-hero { position: relative; min-height: 100svh; display: grid; overflow: hidden; }
.cap-hero__media { position: absolute; inset: -8%; width: 116%; height: 116%; object-fit: cover; }
/* The hero's dark veil used to live here. It was unconditional, so a background could never
   be the colour it was set to. A gradient layer replaces it where a wash is actually wanted. */
.cap-hero__content { position: relative; z-index: 1; min-height: 100svh; padding: clamp(1.5rem, 4vw, 4rem); display: flex; flex-direction: column; justify-content: space-between; }
.cap-hero__top { display: flex; justify-content: space-between; }
.cap-hero__logo { width: min(13rem, 28vw); height: 3rem; object-fit: contain; object-position: right top; }
.cap-hero__copy { padding-top: 20vh; }
.cap-hero__copy > p { margin-top: 2rem; font-size: clamp(1.15rem, 2vw, 1.65rem); max-width: 32rem; }
.cap-hero__copy .cap-hero__ack { font-size: 1rem; max-width: 52rem; }
.cap-scroll-cue { align-self: flex-end; display: flex; align-items: center; gap: .75rem; text-transform: uppercase; font-size: .7rem; letter-spacing: .15em; }
.cap-scroll-cue i { width: 4rem; height: 1px; background: currentColor; }
.cap-text__grid { display: grid; grid-template-columns: minmax(140px, 1fr) 3fr; gap: 3rem; align-items: start; }
.cap-text__copy { max-width: 1050px; text-align: var(--cap-align); }
.cap-text__copy h2 { max-width: 12ch; margin-left: var(--cap-align-offset, 0); }
.cap-richtext { margin-top: clamp(3rem, 6vw, 6rem); columns: 2; column-gap: 4rem; max-width: 850px; font-size: clamp(1.1rem, 1.6vw, 1.5rem); }
.cap-richtext p { margin-bottom: 1.25em; break-inside: avoid; }
.cap-image-grid, .cap-stat-grid { display: grid; grid-template-columns: repeat(var(--cap-columns), minmax(0, 1fr)); }
/* The preset also lands on --cap-gap, because a simple grid has to subtract the gaps itself
   to work out how wide one slot is. */
.cap-gap-none { --cap-gap: 0px; }.cap-gap-small { --cap-gap: .5rem; }.cap-gap-medium { --cap-gap: clamp(1rem, 2vw, 2rem); }.cap-gap-large { --cap-gap: clamp(2rem, 4vw, 4rem); }
.cap-gap-none, .cap-gap-small, .cap-gap-medium, .cap-gap-large { gap: var(--cap-gap); }
.cap-image-grid { grid-auto-flow: dense; }
.cap-image-grid figure { position: relative; min-width: 0; }
.cap-image-grid .cap-interactive { position: relative; overflow: hidden; }
.cap-image-grid img, .cap-image-grid video { display: block; width: 100%; aspect-ratio: var(--cap-cell-ratio, 4 / 5); min-height: 0; object-fit: cover; }
.cap-image-grid .cap-empty-asset { display: block; width: 100%; aspect-ratio: var(--cap-cell-ratio, 4 / 5); min-height: 0; object-fit: cover; background: #d6d8d0; }
.cap-grid-stacked { grid-template-columns: minmax(0, 1fr); }
.cap-grid-runningBond, .cap-grid-mosaic, .cap-grid-custom { grid-template-columns: repeat(12, minmax(0, 1fr)); }
.cap-grid-runningBond figure:nth-child(4n + 1), .cap-grid-runningBond figure:nth-child(4n + 4) { grid-column: span 5; }
.cap-grid-runningBond figure:nth-child(4n + 2), .cap-grid-runningBond figure:nth-child(4n + 3) { grid-column: span 7; }
.cap-grid-mosaic figure:nth-child(6n + 1), .cap-grid-mosaic figure:nth-child(6n + 4) { grid-column: span 8; }
.cap-grid-mosaic figure:nth-child(6n + 2), .cap-grid-mosaic figure:nth-child(6n + 3) { grid-column: span 4; }
.cap-grid-mosaic figure:nth-child(6n + 5), .cap-grid-mosaic figure:nth-child(6n + 6) { grid-column: span 6; }
.cap-grid-custom figure { grid-column: span var(--cap-cell-span, 6); }

/* Grid block. Cells flow into bands — rows one way, columns the other — and every band is
   normalised to the full width, so a composition cannot leave a hole. Each cell grows by
   its share against a zero basis, which is what makes the browser divide the space left
   after the gaps in exactly the authored proportions. */
.cap-grid { display: flex; }
/* A pixel gap is written inline and wins. This is what carries a *preset* gap down into the
   band, which only ever had it between the bands and not between the cells within one. */
.cap-grid__band { position: relative; display: flex; min-width: 0; min-height: 0; gap: var(--cap-gap, 0px); }
/* Drag the seam to set the whole row's height (or the column's width running the other
   way). Every picture on that line follows, so the band stays flush. */
.cap-grid__band-resize { position: absolute; z-index: 5; background: var(--cap-accent); box-shadow: 0 1px 5px rgba(0,0,0,.35); border-radius: 3px; touch-action: none; opacity: 0; transition: opacity .12s ease; }
.cap-grid__band-resize--y { left: 50%; bottom: calc(-4px * var(--cap-handle-scale, 1)); width: calc(46px * var(--cap-handle-scale, 1)); height: calc(7px * var(--cap-handle-scale, 1)); margin-left: calc(-23px * var(--cap-handle-scale, 1)); cursor: ns-resize; }
.cap-grid__band-resize--x { top: 50%; right: calc(-4px * var(--cap-handle-scale, 1)); width: calc(7px * var(--cap-handle-scale, 1)); height: calc(46px * var(--cap-handle-scale, 1)); margin-top: calc(-23px * var(--cap-handle-scale, 1)); cursor: ew-resize; }
.cap-grid.is-editable .cap-grid__band:hover .cap-grid__band-resize, .cap-grid__band-resize:active { opacity: 1; }
.cap-grid__cell { position: relative; min-width: 0; min-height: 0; margin: 0; }
.cap-grid__cell > .cap-interactive { display: block; width: 100%; height: 100%; position: relative; overflow: hidden; }
/* The shape sits on the media wrapper, never on the <img>: giving an image both a definite
   width and a definite height makes the browser drop aspect-ratio entirely, which is why
   picking a cell shape did nothing once a grid had definite row heights. On the wrapper it
   also leaves the caption outside the ratio, where it has always sat, and it is what lets
   one click re-neaten a grid whose cells were dragged to spans of their own. */
/* The shape sizes the band, which then gives every cell in it the same height. Putting the
   ratio on each cell instead made a wide cell taller than a narrow one, and those uneven
   heights were the gaps. Cells stretch to the band and the picture crops to fill. */
.cap-grid--shaped .cap-grid__cell { display: flex; flex-direction: column; align-self: stretch; }
.cap-grid--shaped .cap-grid__cell > .cap-interactive { flex: 1; height: auto; min-height: 0; aspect-ratio: auto; }
/* Every cell in a simple grid is one slot wide, so the shape goes on the cell and comes out
   exactly right. On the band it would have to guess at the gaps between the cells. */
.cap-grid--simple.cap-grid--shaped .cap-grid__band { align-items: start; }
.cap-grid--simple.cap-grid--shaped .cap-grid__cell > .cap-interactive { flex: none; aspect-ratio: var(--cap-cell-ratio, 4 / 5); }
.cap-grid__cell img, .cap-grid__cell video { display: block; width: 100%; height: 100%; min-height: 0; object-fit: cover; }
.cap-grid__cell .cap-empty-asset { display: block; width: 100%; height: 100%; min-height: 0; object-fit: cover; background: #d6d8d0; }

/* Cover-reveal: a zoom-out on a cell picture reveals the whole image instead of only shrinking
   the crop. The wrapper becomes a size container so the media can be sized in cqw/cqh; the media
   is laid out at its own aspect (never stretched), covering the cell at 100% and letterboxing as
   it shrinks below. `--cap-cover-aspect` is set on load (see `initCoverReveal` / the editor ref).
   Until it is — or with scripting off — the `:not(.is-cover-ready)` rule keeps the old crop, so
   nothing regresses; that rule also carries scale/position so the crop still honours the edit. */
.cap-grid__cell > .cap-interactive { container-type: size; }
.cap-grid__cell img.cap-cover-reveal:not(.is-cover-ready),
.cap-grid__cell video.cap-cover-reveal:not(.is-cover-ready) {
  object-fit: cover;
  object-position: calc(var(--cap-media-x, 50) * 1%) calc(var(--cap-media-y, 50) * 1%);
  transform: scale(var(--cap-media-scale, 1));
  transform-origin: calc(var(--cap-media-x, 50) * 1%) calc(var(--cap-media-y, 50) * 1%);
}
.cap-grid__cell img.cap-cover-reveal.is-cover-ready,
.cap-grid__cell video.cap-cover-reveal.is-cover-ready {
  --cap-cover-w: calc(var(--cap-media-scale, 1) * max(100cqw, 100cqh * var(--cap-cover-aspect)));
  position: absolute;
  left: 50%;
  top: 50%;
  width: var(--cap-cover-w);
  height: calc(var(--cap-cover-w) / var(--cap-cover-aspect));
  max-width: none;
  object-fit: fill;
  transform:
    translate(-50%, -50%)
    translate(
      calc((0.5 - var(--cap-media-x, 50) / 100) * max(0px, var(--cap-cover-w) - 100cqw)),
      calc((0.5 - var(--cap-media-y, 50) / 100) * max(0px, (var(--cap-cover-w) / var(--cap-cover-aspect)) - 100cqh))
    );
}
.cap-grid__cell--text > .cap-interactive { display: grid; align-content: center; }
.cap-grid__text { width: 100%; overflow-wrap: anywhere; }
.cap-grid__label { margin: .6rem 0 0; font-family: var(--cap-font-caption, var(--cap-font-body)); font-size: .82rem; letter-spacing: .08em; text-transform: uppercase; }
.cap-grid__empty { display: grid; place-items: center; width: 100%; height: 100%; min-height: 4rem; color: var(--cap-muted); background: color-mix(in srgb, var(--cap-muted) 14%, transparent); font-size: .74rem; letter-spacing: .08em; text-transform: uppercase; }
.cap-grid figcaption { display: flex; gap: 1rem; padding-top: .9rem; font-family: var(--cap-font-caption, var(--cap-font-body)); font-size: .82rem; text-transform: uppercase; letter-spacing: .08em; }
.cap-grid figcaption span { color: var(--cap-muted); }
.cap-grid.cap-caption-overlay figcaption { position: absolute; left: 0; right: 0; bottom: 0; padding: .8rem; color: var(--cap-white); background: linear-gradient(transparent, rgba(0,0,0,.6)); }
.cap-grid.is-editable .cap-grid__cell { cursor: pointer; outline: 1px dashed color-mix(in srgb, var(--cap-muted) 45%, transparent); outline-offset: 2px; }
.cap-grid.is-editable .cap-grid__cell.is-selected { outline: calc(2px * var(--cap-handle-scale, 1)) solid var(--cap-accent); outline-offset: calc(3px * var(--cap-handle-scale, 1)); }
/* Drag a cell onto another to rearrange the set. */
.cap-grid.is-editable .cap-grid__cell { cursor: grab; }
.cap-grid.is-editable .cap-grid__cell.is-dragging { opacity: .4; cursor: grabbing; }
/* The handle sits on the edge the drag moves: a width along the trailing side, a height
   along the bottom. Whatever this cell takes, the rest of its band gives up. */
.cap-grid__resize { position: absolute; z-index: 4; background: var(--cap-accent); box-shadow: 0 1px 5px rgba(0,0,0,.35); touch-action: none; border-radius: 3px; }
.cap-grid__resize--x { right: calc(-4px * var(--cap-handle-scale, 1)); top: 50%; width: calc(7px * var(--cap-handle-scale, 1)); height: calc(34px * var(--cap-handle-scale, 1)); margin-top: calc(-17px * var(--cap-handle-scale, 1)); cursor: ew-resize; }
.cap-grid__resize--y { bottom: calc(-4px * var(--cap-handle-scale, 1)); left: 50%; width: calc(34px * var(--cap-handle-scale, 1)); height: calc(7px * var(--cap-handle-scale, 1)); margin-left: calc(-17px * var(--cap-handle-scale, 1)); cursor: ns-resize; }
.cap-grid-cell-resize { position: absolute; right: .45rem; top: .45rem; z-index: 10; width: calc(18px * var(--cap-handle-scale, 1)); height: calc(18px * var(--cap-handle-scale, 1)); border: calc(2px * var(--cap-handle-scale, 1)) solid white; border-radius: calc(4px * var(--cap-handle-scale, 1)); background: var(--cap-accent); box-shadow: 0 2px 8px rgba(0,0,0,.35); cursor: nwse-resize; touch-action: none; }
.cap-image-grid figcaption { display: flex; gap: 1rem; padding-top: .9rem; font-size: .82rem; text-transform: uppercase; letter-spacing: .08em; }
.cap-image-grid figcaption, .cap-video__caption { font-family: var(--cap-font-caption, var(--cap-font-body)); }
.cap-image-grid figcaption span { color: var(--cap-muted); }
.cap-caption-overlay figure { overflow: hidden; }
.cap-caption-overlay figcaption { position: absolute; left: 0; right: 0; bottom: 0; z-index: 2; align-items: flex-end; padding: 3rem 1rem 1rem; color: white; background: linear-gradient(transparent, rgba(0,0,0,.75)); pointer-events: none; }
.cap-caption-overlay figcaption span { color: rgba(255,255,255,.7); }
.cap-stats { background: var(--cap-charcoal); }
.cap-stats .cap-grid { background: rgba(255,255,255,.22); }
/* Auto-ratio Grid/Gallery bands divide a definite frame height with a zero flex basis. Stat
   rows instead take their height from their content, so their bands must keep that intrinsic
   main-axis size or the row collapses while the counter cards overflow it. */
.cap-stats .cap-grid__band { flex: 0 0 auto !important; }
.cap-grid__cell--stat > .cap-interactive { display: block; min-height: 18rem; background: var(--cap-charcoal); }
.cap-grid__stat { min-height: 18rem; padding: clamp(2rem, 4vw, 4rem); display: flex; flex-direction: column; justify-content: flex-start; gap: .75rem; }
.cap-grid__stat-value { font-family: var(--cap-font-accent, var(--cap-font-heading)); font-size: clamp(4rem, 8vw, 8rem); line-height: .85; letter-spacing: -.05em; color: var(--cap-accent); }
.cap-grid__stat-value .cap-layer__text { min-height: 0; }
.cap-grid__stat-label { max-width: 13rem; margin: 0; font-family: var(--cap-font-caption, var(--cap-font-body)); font-size: 1.05rem; }
.cap-grid__cell--feature > .cap-interactive { display: block; min-height: 24rem; background: var(--cap-paper); }
.cap-features .cap-grid { background: var(--cap-muted); }
.cap-grid__feature { min-height: 24rem; padding: clamp(2rem, 4vw, 4rem); }
.cap-grid__feature > span { display: block; margin-bottom: 5rem; color: var(--cap-muted); font-size: .8rem; letter-spacing: .1em; }
.cap-grid__feature h3 { font-family: var(--cap-font-accent, var(--cap-font-heading)); }
.cap-grid__feature p { margin-top: 2rem; max-width: 30rem; font-size: 1.08rem; }
/* Marquee gallery — a single strip of grid cells that scrolls continuously. The viewport is the
   window; the track holds the one authored set plus (at runtime) one clone; only transform moves.
   Base is overflow:auto so no-JS / reduced-motion leaves it manually scrollable — the runtime
   flips it to overflow:hidden only when it takes over the animation. */
.cap-marquee { position: relative; overflow: auto; }
.cap-marquee__track { display: flex; will-change: transform; }
.cap-marquee--horizontal .cap-marquee__track { flex-direction: row; height: 100%; width: max-content; gap: var(--cap-marquee-gap, 24px); }
.cap-marquee--vertical .cap-marquee__track { flex-direction: column; width: 100%; height: max-content; gap: var(--cap-marquee-gap, 24px); }
.cap-marquee__set { display: flex; flex: 0 0 auto; gap: var(--cap-marquee-gap, 24px); }
.cap-marquee--horizontal .cap-marquee__set { flex-direction: row; height: 100%; }
.cap-marquee--vertical .cap-marquee__set { flex-direction: column; width: 100%; }
.cap-marquee__cell { position: relative; margin: 0; min-width: 0; min-height: 0; overflow: hidden; background: transparent; }
.cap-marquee--horizontal .cap-marquee__cell { flex: 0 0 var(--cap-marquee-cell, 320px); height: 100%; }
.cap-marquee--vertical .cap-marquee__cell { flex: 0 0 var(--cap-marquee-cell, 320px); width: 100%; }
.cap-marquee__cell > .cap-interactive { display: block; width: 100%; height: 100%; position: relative; overflow: hidden; container-type: size; }
.cap-marquee__cell img, .cap-marquee__cell video, .cap-marquee__cell .cap-empty-asset { display: block; width: 100%; height: 100%; object-fit: cover; }
.cap-marquee__cell--text > .cap-interactive { display: grid; align-content: center; padding: clamp(1rem, 2vw, 2rem); background: transparent; }
.cap-marquee.is-editable .cap-marquee__cell, .cap-marquee__cell[role="button"] { cursor: pointer; }
.cap-marquee__cell.is-selected { outline: calc(2px * var(--cap-handle-scale, 1)) solid var(--cap-accent); outline-offset: calc(-2px * var(--cap-handle-scale, 1)); }
@media (prefers-reduced-motion: reduce) {
  /* Motion is disabled; the runtime skips the animation, so keep the strip scrollable. */
  .cap-marquee { overflow: auto; }
}

.cap-full-image { position: relative; height: min(85vw, 100svh); min-height: 34rem; overflow: hidden; background: var(--cap-charcoal); }
.cap-full-image > img, .cap-full-image > video, .cap-full-image > .cap-empty-asset, .cap-full-image > .cap-interactive > img, .cap-full-image > .cap-interactive > video, .cap-full-image > .cap-interactive > .cap-empty-asset { position: absolute; inset: -8%; width: 116%; height: 116%; object-fit: cover; }
.cap-full-image > .cap-interactive { position: absolute; inset: 0; overflow: hidden; }
.cap-full-image > p { position: absolute; left: clamp(1.5rem, 4vw, 4rem); bottom: clamp(1.5rem, 4vw, 4rem); z-index: 1; color: white; font-size: clamp(1.2rem, 2vw, 1.8rem); }
.cap-video video, .cap-video__poster { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; background: #0b0e0c; display: block; }
.cap-video__player { position: relative; overflow: hidden; }
.cap-video__poster { position: relative; overflow: hidden; }
.cap-video__poster img { width: 100%; height: 100%; object-fit: cover; }
.cap-play { position: absolute; inset: 50% auto auto 50%; transform: translate(-50%,-50%); width: 6rem; height: 6rem; border-radius: 50%; background: var(--cap-accent); color: var(--cap-ink); display: grid; place-items: center; text-transform: uppercase; font-size: .72rem; letter-spacing: .12em; }
.cap-video__caption { margin-top: 1rem; font-size: .8rem; text-transform: uppercase; letter-spacing: .1em; }
.cap-empty-asset { display: grid; place-items: center; color: var(--cap-muted); min-height: 18rem; font-size: .8rem; text-transform: uppercase; letter-spacing: .1em; }

/* Positioning context for layers: its width is the canvas width, its height the section height. */
.cap-layer-grid { position: absolute; inset: 0; z-index: 12; pointer-events: none; }
/* The two section-level overlays deliberately do NOT form their own stacking context (no z-index):
   if they did, a layer's `mix-blend-mode` (e.g. an image set to Multiply) could only blend with the
   other layers, never with the section background behind the overlay — so white never knocked out
   onto the background colour. Flattened here, a blended layer blends with everything behind it in
   the section (content and background). Order is preserved by the layers' own z-index instead: they
   are lifted above the content `<section>` (z-index 1) and extend-layers above cropped ones — see
   `stackIndex` in `LayerOverlay`. Frame artboards keep the base z-index; their own transform
   isolates them, so a framed composition blends inside the frame — against the frame's own
   painted background when the section's is a solid colour (see `.cap-frame`). */
.cap-layer-grid--crop, .cap-layer-grid--extend { z-index: auto; }
.cap-layer-grid--crop { overflow: hidden; }
/* Full-bleed sections (`CanvasConfig.fullBleed`): on a window wider than the canvas the background
   runs out to the window edges, and the layer grid's crop widens with it (a negative clip-path
   inset) so glows and overhanging pictures reach the edge too. `initFullBleed` measures the two
   overhangs, in the page's own px; both are 0 on a window no wider than the canvas. */
.cap-page[data-full-bleed="true"] .cap-section-frame[data-bleeding="true"] > .cap-section-background { left: calc(-1 * var(--cap-bleed-left, 0px)); right: calc(-1 * var(--cap-bleed-right, 0px)); }
.cap-page[data-full-bleed="true"] .cap-section-frame[data-bleeding="true"] > .cap-layer-grid > .cap-layer[data-span="full"] { left: calc(-1 * var(--cap-bleed-left, 0px)) !important; right: auto !important; width: calc(100% + var(--cap-bleed-left, 0px) + var(--cap-bleed-right, 0px)) !important; }
.cap-page[data-full-bleed="true"] .cap-section-frame[data-bleeding="true"] > .cap-layer-grid--crop { overflow: visible; clip-path: inset(0 calc(-1 * var(--cap-bleed-right, 0px)) 0 calc(-1 * var(--cap-bleed-left, 0px))); }
.cap-layer-grid--extend { overflow: visible; }
/* A guide, not the coordinate system: columns track width, rows track height, so neither axis misreports the other. */
/* Snap guides: editor chrome, drawn only while a layer is being dragged. */
.cap-snap-guides { position: absolute; inset: 0; z-index: 40; pointer-events: none; }
.cap-snap-guide { position: absolute; box-shadow: 0 0 4px currentColor; }
.cap-snap-guide--x { width: calc(2px / var(--cap-editor-scale, 1)); }
.cap-snap-guide--y { height: calc(2px / var(--cap-editor-scale, 1)); }
.cap-snap-guide--canvas { color: #ff3d8b; background: currentColor; }
.cap-snap-guide--layer { color: #35e0ff; background: currentColor; }
.cap-snap-guide--spacing { color: #ffb020; background: currentColor; }
.cap-snap-guide--spacing::after { content: attr(data-gap) "px"; position: absolute; left: 4px; top: 4px; padding: 1px 4px; color: #2a1a00; background: #ffb020; border-radius: 2px; font: 700 9px/1.4 Arial, sans-serif; white-space: nowrap; }
.cap-layer-grid--crop.is-editable { background-image: linear-gradient(to right, rgba(47,92,72,.34) 1px, transparent 1px), linear-gradient(to bottom, rgba(47,92,72,.28) 1px, transparent 1px); background-size: calc(100% / var(--cap-guide-columns, 12)) calc(100% / var(--cap-guide-rows, 10)); }
/* The overlay is a guide, not the coordinate system, so it can simply be switched off. */
[data-guides="off"] .cap-layer-grid--crop.is-editable { background-image: none; }
/* Rotating text: every phrase is stacked in the same grid cell so the box is sized to the largest
   and never jumps as they swap; the runtime (initRotatingText) fades the active one in. */
.cap-rotating-text { display: grid; }
.cap-rotating-text__entry { grid-area: 1 / 1; opacity: 0; transition: opacity .55s ease; }
.cap-rotating-text__entry[data-active] { opacity: 1; }
/* Only the live (preview/export) instance is clickable-to-advance; while editing (the layer grid
   carries `is-editable`, and the runtime that handles the click is not attached) it is not. */
.cap-layer-grid:not(.is-editable) [data-rotating-text] { cursor: pointer; }
@media (prefers-reduced-motion: reduce) { .cap-rotating-text__entry { transition: none; } }

/* Rotating frame: the artboard's direct segment children are stacked on top of each other so the
   frame stays one fixed box; the runtime (initRotatingFrame) crossfades the active segment in.
   Live (preview/export) only — while editing we show one segment at a time via the dots UI. */
[data-rotating-frame] { cursor: pointer; }
[data-rotating-frame] > .cap-layer { transition: opacity .55s ease; }
@media (prefers-reduced-motion: reduce) { [data-rotating-frame] > .cap-layer { transition: none; } }

/* Peek: the previous/next segments sit scaled-down, faint and blurred just past the frame's edges
   (positioned by the runtime), so the transition also carries the slide and the blur. The active
   one sits centred and sharp on top; the visible slivers invite a click. */
[data-rotating-peek] > .cap-layer {
  transition: transform .55s cubic-bezier(.4,0,.2,1), opacity .55s ease, filter .55s ease;
  will-change: transform; transform-origin: center; cursor: pointer;
}
@media (prefers-reduced-motion: reduce) { [data-rotating-peek] > .cap-layer { transition: opacity .55s ease; } }

/* Segment editor chrome (edit mode only): dots to switch the segment being edited, a minus to
   delete the active one, a plus to add one. Sits stacked on the right of the frame — off to the
   side rather than above or below it — so it never covers the composition being edited. */
.cap-rotating-frame__dots {
  position: absolute; top: 50%; right: calc(-34px * var(--cap-handle-scale, 1)); transform: translateY(-50%);
  display: flex; flex-direction: column; gap: calc(4px * var(--cap-handle-scale, 1)); align-items: center;
  padding: calc(5px * var(--cap-handle-scale, 1)) calc(3px * var(--cap-handle-scale, 1));
  background: var(--cap-panel, #fff); border: 1px solid color-mix(in srgb, var(--cap-muted) 40%, transparent);
  border-radius: 999px; box-shadow: 0 2px 8px rgba(0,0,0,.14); z-index: 6; pointer-events: auto;
}
.cap-rotating-frame__dots button {
  min-width: calc(18px * var(--cap-handle-scale, 1)); height: calc(18px * var(--cap-handle-scale, 1));
  padding: 0 calc(4px * var(--cap-handle-scale, 1)); border-radius: 999px; cursor: pointer;
  border: 1px solid color-mix(in srgb, var(--cap-muted) 45%, transparent); background: transparent;
  color: var(--cap-text, #1c1c1c); font-size: calc(11px * var(--cap-handle-scale, 1)); line-height: 1;
  display: grid; place-items: center;
}
.cap-rotating-frame__dots button.is-active { background: var(--cap-accent); border-color: var(--cap-accent); color: #fff; }
.cap-rotating-frame__dots .cap-rotating-frame__add,
.cap-rotating-frame__dots .cap-rotating-frame__remove { font-weight: 700; }
.cap-rotating-frame__dots .cap-rotating-frame__remove {
  color: color-mix(in srgb, var(--cap-text, #1c1c1c) 70%, #c0392b 30%);
  border-color: color-mix(in srgb, var(--cap-muted) 45%, #c0392b 15%);
}
.cap-rotating-frame__dots .cap-rotating-frame__remove:disabled { opacity: .35; cursor: not-allowed; }

/* Viewer-facing navigation dots (one per segment). Default spot: centred just below the frame; the
   inline transform adds the author's own offset. Scales with the frame (it lives in the artboard).
   The active dot stretches into a pill and takes the accent, so a glance says "there are several,
   and this is the one". Kept clickable while the segments themselves stay non-interactive. */
.cap-rotating-frame__pager {
  position: absolute; left: 50%; top: 100%; margin-top: calc(16px * var(--cap-handle-scale, 1));
  display: flex; gap: calc(10px * var(--cap-handle-scale, 1)); align-items: center; justify-content: center;
  z-index: 7; pointer-events: auto;
}
.cap-rotating-frame__page {
  width: calc(9px * var(--cap-handle-scale, 1)); height: calc(9px * var(--cap-handle-scale, 1));
  padding: 0; border-radius: 999px; cursor: pointer;
  border: none; background: color-mix(in srgb, var(--cap-text, #1c1c1c) 28%, transparent);
  transition: width .35s cubic-bezier(.4,0,.2,1), background-color .35s ease, opacity .35s ease;
}
.cap-rotating-frame__page:hover { background: color-mix(in srgb, var(--cap-text, #1c1c1c) 50%, transparent); }
.cap-rotating-frame__page.is-active { width: calc(26px * var(--cap-handle-scale, 1)); background: var(--cap-accent); }
@media (prefers-reduced-motion: reduce) { .cap-rotating-frame__page { transition: none; } }

/* Position, size and transform come from the placement model as inline styles. */
.cap-layer { min-width: 0; min-height: 0; transform-origin: center; pointer-events: none; }
/* Only the editing canvas claims touches on a layer (so a finger drags it instead of scrolling). On
   the published page a layer must let the browser scroll: `touch-action: none` here once stopped
   every phone from scrolling wherever a finger landed on a picture, film or line of text. */
.cap-layer-grid.is-editable .cap-layer { touch-action: none; }
/* A sticky layer's content target is transformed every frame by initStickyLayers — hint the
   compositor so the hold reads smoothly. Only a hint; the runtime owns the actual transform. */
.cap-layer[data-sticky="true"] > .cap-animate-group, .cap-layer[data-sticky="true"] > .cap-keyframe-target { will-change: transform; }
/* Phase 5's cursor parallax: the CSS transition gives the pointer-follow a soft lag for a depth
   feel — initCursorParallax only ever sets the final translate(); this selector can never match a
   keyframed or sticky target, since a layer is excluded from data-cursor-parallax at the source
   (./index) whenever it has either of those. */
.cap-layer[data-cursor-parallax="true"] > .cap-animate-group, .cap-layer[data-cursor-parallax="true"] > .cap-keyframe-target { transition: transform .35s cubic-bezier(.16,1,.3,1); will-change: transform; }
.cap-layer-grid.is-editable .cap-layer { pointer-events: auto; cursor: grab; }
.cap-layer-grid.is-editable .cap-layer:active { cursor: grabbing; }
/* Editor chrome counter-scales against `--cap-handle-scale` (set by the editor to 1/stage-scale,
   default 1) so every handle, outline and grab-dot keeps a constant *on-screen* size at any zoom
   — otherwise the stage transform would blow a 7px handle up to 14px at 200% or shrink it to a
   few px when fit-zoomed out. Both the size and the offset that centres each handle on its corner
   are scaled by the same factor, so they stay put relative to the box. */
.cap-layer.is-selected { outline: calc(2px * var(--cap-handle-scale, 1)) solid var(--cap-accent); outline-offset: calc(3px * var(--cap-handle-scale, 1)); }
/* When more than one layer is selected, the group box owns the outline and handles, so
   each member's own selection chrome is hidden to avoid a noisy stack of boxes. */
.cap-layer.is-group-member { outline: none; }
/* The alignment anchor — the last-selected layer, which the edge aligns line everything else
   up to and which itself never moves. It keeps a distinct solid outline even amongst the
   otherwise outline-less group members, plus a small corner dot, so the person can see which
   layer is the reference before pressing an align. Editor-only: the export never sets it. */
.cap-layer.is-anchor { outline: calc(2px * var(--cap-handle-scale, 1)) solid var(--cap-accent); outline-offset: calc(3px * var(--cap-handle-scale, 1)); }
.cap-layer.is-anchor::after {
  content: "";
  position: absolute;
  top: calc(-6px * var(--cap-handle-scale, 1));
  left: calc(-6px * var(--cap-handle-scale, 1));
  width: calc(8px * var(--cap-handle-scale, 1));
  height: calc(8px * var(--cap-handle-scale, 1));
  border-radius: 50%;
  background: var(--cap-accent);
  border: calc(1.5px * var(--cap-handle-scale, 1)) solid #fff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, .4);
  z-index: 21;
  pointer-events: none;
}
/* A locked layer is excluded from canvas selection entirely, not just dragging — a click where
   it sits must reach whatever unlocked layer (or the section background) sits behind it instead,
   so a locked object never blocks selecting something stacked underneath. `pointer-events: none`
   is what makes that click fall through: the browser's hit-testing skips straight past this
   element to whatever is next, no JS hit-testing required. One extra class selector's
   specificity is enough to win over `.cap-layer-grid.is-editable .cap-layer`'s own
   `pointer-events: auto` two rules up, so no `!important` is needed. The Layers panel is
   untouched by this — its own click handler selects by id directly, never through the canvas —
   so a locked layer stays reachable there. */
.cap-layer-grid.is-editable .cap-layer.is-locked { pointer-events: none; }
.cap-layer > .cap-interactive { width: 100%; height: 100%; display: block; }
/* The shadow sits behind the real content within the same stacking context `.cap-layer`
   already gets from its own z-index (see `zIndex: stackIndex`) — -1 here keeps it contained
   to this layer's slot rather than bleeding behind neighbouring layers or sections. */
.cap-layer__shadow { position: absolute; inset: 0; z-index: -1; pointer-events: none; }
/* Image scale is a crop, not a resize: the picture grows inside the box it was given and
   is clipped by it, rather than spilling out and leaving the box looking too small. Only
   media is clipped — text has to stay free to overflow, which is how `auto` height works. */
.cap-layer-image > .cap-interactive { overflow: hidden; }
.cap-layer__image, .cap-layer > .cap-interactive > .cap-empty-asset { width: 100%; height: 100%; min-height: 1px; display: block; object-fit: cover; }
.cap-layer__shape { width: 100%; height: 100%; min-height: 0; display: block; }
.cap-layer__icon { width: 100%; height: 100%; min-height: 1px; display: block; }
.cap-layer__icon svg { width: 100%; height: 100%; display: block; }
/* Lucide has no root fill and expects a currentColor stroke. Custom SVG paints have already
   been normalised server-side, so forcing a stroke here would incorrectly outline fill-only
   artwork. */
.cap-layer__icon:not(.cap-layer__icon--custom) svg { stroke: currentColor; }
.cap-layer__line { width: 100%; height: 100%; min-height: 1px; display: block; border-radius: 999px; }
/* The plain data table layer. A CSS grid of authored column widths / row heights; the layer box is
   kept the same size by the store, so the tracks (px) fill it exactly. Fully transparent — no
   fill, no gridlines, no header tint — so only the cell text ever paints; the table is purely a
   text-alignment device, in the export and the read-only preview alike. Cell boundaries while
   actively editing come from a non-painting `outline` (like `.cap-grid__cell` below), which never
   renders outside the editor. The resize/add chrome is likewise editor-only (`.cap-table.is-editable`). */
.cap-table { position: relative; display: grid; box-sizing: border-box; width: 100%; height: 100%; color: var(--cap-ink); font: 400 13px/1.35 var(--cap-font-body, inherit); overflow: visible; }
.cap-table__cellwrap { position: relative; min-width: 0; box-sizing: border-box; overflow: hidden; }
.cap-table.is-editable .cap-table__cellwrap { outline: 1px dashed color-mix(in srgb, var(--cap-muted) 45%, transparent); outline-offset: -1px; }
.cap-table__cell { width: 100%; height: 100%; box-sizing: border-box; padding: 4px 8px; overflow: hidden; white-space: pre-wrap; overflow-wrap: anywhere; outline: none; }
.cap-table__cell--header { font-weight: 600; }
.cap-table__cell.is-editing { overflow: visible; box-shadow: inset 0 0 0 2px var(--cap-accent); }
/* Off-screen natural-size copy of the cell text, so auto-fit can measure content width/height
   (`white-space: pre` keeps single lines whole and honours explicit line breaks). */
.cap-table__measure { position: absolute; top: 0; left: 0; visibility: hidden; pointer-events: none; padding: 4px 8px; white-space: pre; font: inherit; }
.cap-table.is-editable .cap-table__resize { position: absolute; z-index: 6; touch-action: none; }
.cap-table.is-editable .cap-table__resize--col { top: 0; bottom: 0; width: 9px; transform: translateX(-50%); cursor: col-resize; }
.cap-table.is-editable .cap-table__resize--row { left: 0; right: 0; height: 9px; transform: translateY(-50%); cursor: row-resize; }
.cap-table.is-editable .cap-table__resize:hover { background: color-mix(in srgb, var(--cap-accent) 45%, transparent); }
.cap-table.is-editable .cap-table__add { position: absolute; z-index: 6; display: grid; place-items: center; width: 18px; height: 18px; padding: 0; border: 1px solid var(--cap-table-line); border-radius: 50%; background: var(--cap-white); color: var(--cap-ink); font: 600 13px/1 var(--cap-font-body, sans-serif); cursor: pointer; box-shadow: 0 1px 4px rgba(0,0,0,.18); }
.cap-table.is-editable .cap-table__add:hover { border-color: var(--cap-accent); color: color-mix(in srgb, var(--cap-ink) 70%, var(--cap-accent)); }
.cap-table.is-editable .cap-table__add--col { left: calc(100% + 6px); top: 50%; transform: translateY(-50%); }
.cap-table.is-editable .cap-table__add--row { top: calc(100% + 6px); left: 50%; transform: translateX(-50%); }
/* A frame's contents are laid out at the design size and transformed as one, so the composition
   holds instead of reflowing. Ordinary frames use one uniform scale; grouped compositions may
   opt into independent X/Y factors. The nested fallbacks keep older exports and normal frames
   on the original uniform path.
   The artboard is also its own stacking context (the transform), so a child's `mix-blend-mode`
   can only blend with what is painted inside the frame. When the section background is a solid
   colour the artboard paints that same colour as its own background (--cap-frame-bg, set by
   `frameBackgroundStyle` in index.tsx): an element's own background paints at the bottom of its
   own stacking context, beneath its children, so a picture set to Multiply now has that colour
   as its backdrop and knocks white out onto it — as if the frame were not there. The
   transparent fallback leaves every other frame exactly as it was. */
.cap-frame { position: relative; transform-origin: 0 0; transform: scale(var(--cap-frame-scale-x, var(--cap-frame-scale, 1)), var(--cap-frame-scale-y, var(--cap-frame-scale, 1))); background-color: var(--cap-frame-bg, transparent); }
/* An editable frame shows its artboard extents so its bounds are always visible while
   composing — a frame is easy to lose against a busy section otherwise. */
.cap-frame.is-editable { outline: calc(1.5px * var(--cap-handle-scale, 1)) dashed color-mix(in srgb, var(--cap-accent) 55%, #5a5f5a); outline-offset: calc(1px * var(--cap-handle-scale, 1)); box-shadow: inset 0 0 0 1px rgba(0,0,0,.12); }
/* The frame holder is the entrance-animation target and the artboard's containing block; it
   fills the layer so the artboard still measures and scales against the layer's real size. */
.cap-frame-holder { position: absolute; inset: 0; }
.cap-layer-frame .cap-frame { pointer-events: none; }
.cap-layer-frame.cap-layer .cap-frame.is-editable { pointer-events: auto; }
.cap-frame__empty { position: absolute; inset: 0; display: grid; place-items: center; color: var(--cap-muted); border: 1px dashed color-mix(in srgb, var(--cap-muted) 55%, transparent); font-size: .8rem; text-transform: uppercase; letter-spacing: .1em; }
.cap-layer > .cap-grid { width: 100%; height: 100%; }
.cap-layer__text { width: 100%; height: auto; min-height: 100%; overflow: visible; white-space: pre-wrap; overflow-wrap: anywhere; word-break: normal; text-wrap: wrap; }
/* An inline link inside a text layer. Unlike the layer-level wrapper it carries no class of
   its own, so without this it would take the browser's blue and fight the brand colours the
   whole point of the palette is to control. It inherits the layer's colour instead, and the
   underline is then the only thing marking it as a link, so it stays. */
.cap-layer__text a { color: inherit; text-decoration: underline; text-underline-offset: .15em; }
/* Split-text reveals (lineRise / wordCascade / letterStagger). The runtime wraps each word (and,
   for the letter reveal, each glyph) in these while `effects.js` is active, then restores the
   original markup on teardown — so the static HTML stays plain text and a JS-less view shows it
   whole. `inline-block` gives each piece a transformable box; the real spaces between words stay
   as text nodes, so the line still wraps naturally and reads in order for a screen reader.
   The line reveal's word clips its own inner so it can rise from behind its edge. The clip is a
   `clip-path`, NOT `overflow: hidden` — an inline-block whose overflow computes to anything but
   `visible` uses its bottom margin edge as its baseline, which shoves the word box above the
   line's natural top and grows every line box (the text's line height visibly changes). A
   clip-path clips painting only, so the baseline stays the word's own text baseline and the
   layout is byte-for-byte the plain text's. The negative insets give a hair of breathing room
   (the same job the old padding/margin pair did) so a descender or accent is never shaved. */
.cap-split-word, .cap-split-inner, .cap-split-letter { display: inline-block; }
.cap-split-inner, .cap-split-letter { will-change: transform; }
.cap-split-word--mask { clip-path: inset(-.06em -.06em -.1em -.06em); }
.cap-layer__shape.shape-circle { border-radius: 50%; aspect-ratio: 1; }
.cap-layer__shape.shape-line { height: 2px; align-self: center; margin-top: 50%; }
/* Ambient shapes (`AmbientShapeConfig`): the fill is painted on an inner element so the blur and
   the slow motions never collide with the entrance tween on the outer one. Drift, breathe and morph
   animate three different properties (`translate`, `scale`, `border-radius`), so they compose;
   the first two stay on the compositor, morph repaints. Still while editing and for reduced motion. */
.cap-layer__shape.is-ambient { position: relative; background: none; overflow: visible; }
.cap-ambient {
  position: absolute; inset: 0; display: block; border-radius: inherit;
  filter: blur(var(--cap-ambient-blur, 80px)); will-change: translate, scale;
}
.cap-page.is-editable .cap-ambient { animation: none !important; }
@keyframes cap-ambient-drift {
  0% { translate: 0 0; }
  33% { translate: calc(var(--cap-ambient-drift, 60px) * .7) calc(var(--cap-ambient-drift, 60px) * -.45); }
  66% { translate: calc(var(--cap-ambient-drift, 60px) * -.5) calc(var(--cap-ambient-drift, 60px) * .6); }
  100% { translate: calc(var(--cap-ambient-drift, 60px) * .25) calc(var(--cap-ambient-drift, 60px) * .35); }
}
@keyframes cap-ambient-breathe {
  0% { scale: 1; }
  50% { scale: 1.12 .94; }
  100% { scale: .95 1.08; }
}
@keyframes cap-ambient-morph {
  0% { border-radius: 42% 58% 63% 37% / 41% 44% 56% 59%; }
  50% { border-radius: 62% 38% 34% 66% / 58% 63% 37% 42%; }
  100% { border-radius: 38% 62% 55% 45% / 35% 55% 45% 65%; }
}
@media (prefers-reduced-motion: reduce) { .cap-ambient { animation: none !important; } }
/* One handle per corner. The opposite corner is what stays put while you drag. */
.cap-layer__resize { position: absolute; width: calc(7px * var(--cap-handle-scale, 1)); height: calc(7px * var(--cap-handle-scale, 1)); z-index: 4; border: calc(1.5px * var(--cap-handle-scale, 1)) solid #fff; border-radius: calc(1.5px * var(--cap-handle-scale, 1)); background: var(--cap-accent); box-shadow: 0 1px 5px rgba(0,0,0,.35); touch-action: none; }
.cap-layer__resize--nw { left: calc(-4px * var(--cap-handle-scale, 1)); top: calc(-4px * var(--cap-handle-scale, 1)); cursor: nwse-resize; }
.cap-layer__resize--ne { right: calc(-4px * var(--cap-handle-scale, 1)); top: calc(-4px * var(--cap-handle-scale, 1)); cursor: nesw-resize; }
.cap-layer__resize--sw { left: calc(-4px * var(--cap-handle-scale, 1)); bottom: calc(-4px * var(--cap-handle-scale, 1)); cursor: nesw-resize; }
.cap-layer__resize--se { right: calc(-4px * var(--cap-handle-scale, 1)); bottom: calc(-4px * var(--cap-handle-scale, 1)); cursor: nwse-resize; }
/* Rotate zones ring each corner just outside the resize square. Lower z-index than the square,
   so the pointer resizes on the corner itself and rotates in the band around it. The cursor is an
   inline circular-arrow SVG (white halo under black stroke, so it reads on any background). */
.cap-layer__rotate { position: absolute; width: calc(18px * var(--cap-handle-scale, 1)); height: calc(18px * var(--cap-handle-scale, 1)); z-index: 3; touch-action: none; cursor: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='26' height='26' viewBox='0 0 24 24' fill='none' stroke-linecap='round' stroke-linejoin='round'><g stroke='white' stroke-width='4'><path d='M21 12a9 9 0 1 1-3-6.7L21 8'/><path d='M21 3v5h-5'/></g><g stroke='black' stroke-width='2'><path d='M21 12a9 9 0 1 1-3-6.7L21 8'/><path d='M21 3v5h-5'/></g></svg>") 13 13, crosshair; }
.cap-layer__rotate--nw { left: calc(-15px * var(--cap-handle-scale, 1)); top: calc(-15px * var(--cap-handle-scale, 1)); }
.cap-layer__rotate--ne { right: calc(-15px * var(--cap-handle-scale, 1)); top: calc(-15px * var(--cap-handle-scale, 1)); }
.cap-layer__rotate--sw { left: calc(-15px * var(--cap-handle-scale, 1)); bottom: calc(-15px * var(--cap-handle-scale, 1)); }
.cap-layer__rotate--se { right: calc(-15px * var(--cap-handle-scale, 1)); bottom: calc(-15px * var(--cap-handle-scale, 1)); }
/* Edge-only scale handles: a slim bar that appears on hover and scales just that axis. */
.cap-layer__edge { position: absolute; z-index: 4; opacity: 0; pointer-events: none; background: var(--cap-accent); border: calc(1px * var(--cap-handle-scale, 1)) solid #fff; box-shadow: 0 1px 5px rgba(0,0,0,.35); transition: opacity .12s ease; }
.cap-layer:hover .cap-layer__edge, .cap-layer:focus-within .cap-layer__edge { opacity: 1; pointer-events: auto; }
.cap-layer__edge--top { left: 33.333%; right: 33.333%; top: calc(-3px * var(--cap-handle-scale, 1)); height: calc(6px * var(--cap-handle-scale, 1)); border-radius: calc(3px * var(--cap-handle-scale, 1)); cursor: ns-resize; }
.cap-layer__edge--bottom { left: 33.333%; right: 33.333%; bottom: calc(-3px * var(--cap-handle-scale, 1)); height: calc(6px * var(--cap-handle-scale, 1)); border-radius: calc(3px * var(--cap-handle-scale, 1)); cursor: ns-resize; }
/* OpenAsset Medium/Large size-swap buttons — bottom-left, mirroring the top-left label so
   neither the label nor the four corner resize handles are ever crowded. Editor-only: the
   exporter never supplies `renderImageSizeSwap`, so these classes never match any element in a
   static export. */
.cap-layer__size-swap { position: absolute; left: 0; bottom: -28px; z-index: 5; display: flex; gap: 4px; touch-action: none; }
.cap-layer__size-swap-btn { width: 22px; height: 22px; display: grid; place-items: center; padding: 0; border: 1.5px solid #fff; border-radius: 50%; background: rgba(21, 32, 21, .78); color: #fff; font: 700 9px/1 Arial, sans-serif; box-shadow: 0 1px 5px rgba(0,0,0,.35); cursor: pointer; touch-action: none; }
.cap-layer__size-swap-btn.is-active { background: var(--cap-accent); color: #152015; }
.cap-layer__size-swap-btn:disabled { cursor: default; opacity: .6; }
/* --- PDF layer: edit-mode placeholder, and the live viewer `initPdfViewers` mounts over it
   in Preview mode and the static export. Nav sits below the page content, positioned the
   same way as the M/L size-swap buttons above (absolute, just past the box's own bottom
   edge) rather than overlaid on top of it. */
.cap-pdf-layer { width: 100%; height: 100%; }
.cap-pdf-layer__placeholder { display: grid; place-items: center; gap: 6px; width: 100%; height: 100%; min-height: 4rem; padding: 1rem; color: var(--cap-muted); background: color-mix(in srgb, var(--cap-muted) 14%, transparent); text-align: center; }
.cap-pdf-layer__filename { font-size: .74rem; letter-spacing: .02em; word-break: break-word; }
/* `.cap-layer` defaults to `pointer-events:none` (see above) so idle canvas space never
   steals clicks; this class only ever lands on the container once `initPdfViewers` has
   actually mounted the live viewer into it, so the edit-mode placeholder stays inert. */
.cap-pdf-viewer { position: relative; pointer-events: auto; width: 100%; height: 100%; }
.cap-pdf-viewer__stage { position: relative; width: 100%; height: 100%; overflow: auto; display: flex; align-items: flex-start; justify-content: center; background: color-mix(in srgb, var(--cap-muted) 8%, transparent); }
.cap-pdf-viewer__page { display: block; max-width: 100%; height: auto; transform: scale(var(--cap-pdf-zoom, 1)); transform-origin: top center; transition: transform .15s ease; }
.cap-pdf-viewer__nav { position: absolute; left: 0; right: 0; bottom: -34px; z-index: 5; display: flex; align-items: center; gap: 6px; touch-action: none; }
.cap-pdf-viewer__btn { width: 26px; height: 26px; display: grid; place-items: center; padding: 0; border: 1.5px solid #fff; border-radius: 50%; background: rgba(21, 32, 21, .78); color: #fff; font: 700 13px/1 Arial, sans-serif; box-shadow: 0 1px 5px rgba(0,0,0,.35); cursor: pointer; touch-action: none; }
.cap-pdf-viewer__btn:disabled { cursor: default; opacity: .45; }
.cap-pdf-viewer__position { min-width: 44px; text-align: center; padding: 4px 8px; border-radius: 999px; background: rgba(21, 32, 21, .78); color: #fff; font: 600 10px/1 Arial, sans-serif; }
.cap-pdf-viewer__spacer { flex: 1; }
.cap-pdf-viewer__error { margin: 0; padding: 1rem; text-align: center; color: var(--cap-muted); font-size: .74rem; }
.cap-pdf-viewer__loading { position: absolute; inset: 0; display: grid; place-items: center; color: var(--cap-muted); background: color-mix(in srgb, var(--cap-muted) 8%, transparent); font-size: .74rem; letter-spacing: .04em; }
.cap-layer__edge--left { top: 33.333%; bottom: 33.333%; left: calc(-3px * var(--cap-handle-scale, 1)); width: calc(6px * var(--cap-handle-scale, 1)); border-radius: calc(3px * var(--cap-handle-scale, 1)); cursor: ew-resize; }
.cap-layer__edge--right { top: 33.333%; bottom: 33.333%; right: calc(-3px * var(--cap-handle-scale, 1)); width: calc(6px * var(--cap-handle-scale, 1)); border-radius: calc(3px * var(--cap-handle-scale, 1)); cursor: ew-resize; }
/* The encompassing selection box for a multi-selection. It sits above the layers and
   carries the only resize handles, so the whole group scales as one large object. */
.cap-group-selection { position: absolute; z-index: 20; border: calc(2px * var(--cap-handle-scale, 1)) solid var(--cap-accent); box-shadow: inset 0 0 0 1px rgba(0,0,0,.4); pointer-events: none; }
.cap-group-selection__handle { position: absolute; width: calc(7px * var(--cap-handle-scale, 1)); height: calc(7px * var(--cap-handle-scale, 1)); z-index: 4; border: calc(1.5px * var(--cap-handle-scale, 1)) solid #fff; border-radius: calc(1.5px * var(--cap-handle-scale, 1)); background: var(--cap-accent); box-shadow: 0 1px 5px rgba(0,0,0,.35); touch-action: none; pointer-events: auto; cursor: nwse-resize; }
.cap-group-selection__handle--nw { left: calc(-4px * var(--cap-handle-scale, 1)); top: calc(-4px * var(--cap-handle-scale, 1)); cursor: nwse-resize; }
.cap-group-selection__handle--ne { right: calc(-4px * var(--cap-handle-scale, 1)); top: calc(-4px * var(--cap-handle-scale, 1)); cursor: nesw-resize; }
.cap-group-selection__handle--sw { left: calc(-4px * var(--cap-handle-scale, 1)); bottom: calc(-4px * var(--cap-handle-scale, 1)); cursor: nesw-resize; }
.cap-group-selection__handle--se { right: calc(-4px * var(--cap-handle-scale, 1)); bottom: calc(-4px * var(--cap-handle-scale, 1)); cursor: nwse-resize; }
.cap-media-sound { position: absolute; right: .75rem; bottom: .75rem; z-index: 8; width: 2.1rem; height: 2.1rem; padding: 0; display: grid; place-items: center; color: white; border: 1px solid rgba(255,255,255,.55); border-radius: 50%; background: rgba(0,0,0,.5); backdrop-filter: blur(8px); cursor: pointer; transition: background-color .2s ease; }
.cap-media-sound:hover { background: rgba(0,0,0,.72); }
.cap-media-sound svg { display: block; }

/* The layer wrapping this defaults to pointer-events:none (see `.cap-layer`) so the editor's
   drag surface can own clicks while composing. Outside that editing mode nothing else turns
   it back on, which left every link, lightbox trigger and hover effect dead in preview and
   export. `auto` here re-enables hit-testing for exactly the interactive content itself,
   regardless of whether an ancestor layer is in edit mode. */
/* The hover effects animate the individual `translate`/`scale`/`opacity`/`filter`/… properties
   (see `.cap-hover-*` below), never the `transform` shorthand — that one is owned by the
   keyframe/scroll animation system, which writes `style.transform` every frame. So `transform`
   must NOT be in this transition list: easing it here makes a scroll-driven or drag-authored move
   lag ~0.3s behind the real position (the "ease in/out while dragging" bug). The individual hover
   properties compose with the keyframe `transform` rather than clobbering it, so they stay smooth. */
/* Hover transitions follow the site's motion style (`--cap-ease`/`--cap-pace`, set on .cap-page by
   `CanvasConfig.motion`); the fallbacks are the original .3s ease. */
.cap-interactive { display: block; color: inherit; text-decoration: none; pointer-events: auto; transition: translate calc(.3s * var(--cap-pace, 1)) var(--cap-ease, ease), scale calc(.3s * var(--cap-pace, 1)) var(--cap-ease, ease), opacity calc(.3s * var(--cap-pace, 1)) var(--cap-ease, ease), filter calc(.3s * var(--cap-pace, 1)) var(--cap-ease, ease), background-color calc(.3s * var(--cap-pace, 1)) var(--cap-ease, ease), box-shadow calc(.3s * var(--cap-pace, 1)) var(--cap-ease, ease); }
.cap-hover-zoom, .cap-hover-blur, .cap-hover-sharpen { overflow: hidden; }
/* Phase 5 (dynamism brief): a bare `:hover` fires on tap on touch and can "stick" until the
   visitor taps elsewhere — these are desktop-only interaction cues, not something a touch visitor
   should ever see triggered. `(pointer: fine)` alone is not enough (a stylus or some hybrid
   devices report fine pointers without real hover), so both conditions are required together. */
@media (hover: hover) and (pointer: fine) {
  .cap-hover-highlight:hover { background: color-mix(in srgb, var(--cap-accent) 72%, transparent); color: var(--cap-charcoal); }
  /* This wrapper is also `.cap-animate-group` — the element every entrance/scroll effect is
     pointed at. GSAP does not just set `transform`: on first touch it folds any existing
     `translate`/`scale`/`rotate` into the matrix and writes `translate:none;scale:none;
     rotate:none` inline, and it leaves `opacity`/`filter` inline at their resting values.
     Those inline styles outrank a stylesheet rule, so in Preview and in the static export —
     wherever `initEffects` runs — an animated layer's Lift/Zoom/Fade/Invert silently stopped
     working. `!important` is what lets the hover state win back the property GSAP parked. */
  .cap-hover-lift:hover { translate: 0 -8px !important; }
  .cap-hover-zoom:hover { scale: 1.035 !important; }
  .cap-hover-fade:hover { opacity: .55 !important; }
  .cap-hover-underline:hover { text-decoration: underline; text-decoration-thickness: .08em; text-underline-offset: .16em; }
  .cap-hover-invert:hover { filter: invert(1) !important; }
  .cap-hover-glow:hover { box-shadow: 0 0 0 2px color-mix(in srgb, var(--cap-accent) 75%, transparent), 0 14px 38px color-mix(in srgb, var(--cap-accent) 25%, transparent); }
  /* Filter reveals. `blur` is sharp at rest and softens on hover; `sharpen` and `greyscale`
     carry a resting filter that the hover state clears — their whole pair lives inside this
     media query so a touch visitor, who can never hover to undo it, just sees the plain
     image. Same `!important` as Invert above: all four share the `filter` property GSAP parks
     inline on `.cap-animate-group`, so a scroll-blur layer that also has one of these hover
     effects resolves to the hover rule rather than to GSAP's inline value. */
  .cap-hover-blur:hover { filter: blur(4px) !important; }
  .cap-hover-sharpen { filter: blur(4px) !important; }
  .cap-hover-sharpen:hover { filter: blur(0) !important; }
  .cap-hover-greyscale { filter: grayscale(1) !important; }
  .cap-hover-greyscale:hover { filter: grayscale(0) !important; }
}

@container (max-width: 900px) {
  .cap-section-heading, .cap-text__grid { grid-template-columns: 1fr; gap: 2rem; }
  .cap-richtext { columns: 1; }
  .cap-page h1 { font-size: clamp(3.5rem, 12vw, 6.8rem); }
  .cap-page h2 { font-size: clamp(3rem, 9vw, 5.5rem); }
  .cap-image-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cap-image-grid figure { grid-column: span 1 !important; }
}

@container (max-width: 640px) {
  .cap-bound { width: calc(100% - 2rem); }
  .cap-pad { padding-block: 5.5rem; }
  .cap-hero__content { padding: 1rem; }
  .cap-hero__copy { padding-top: 28vh; }
  .cap-section-heading { margin-bottom: 3.5rem; }
  .cap-richtext { font-size: 1.05rem; }
  .cap-grid__cell--stat > .cap-interactive, .cap-grid__stat { min-height: 14rem; }
  .cap-grid__cell--feature > .cap-interactive, .cap-grid__feature { min-height: 20rem; }
  .cap-image-grid { grid-template-columns: minmax(0, 1fr); }
  .cap-image-grid figure { grid-column: 1 / -1 !important; }
}

/* Section side menu (`CanvasConfig.sectionNav`). The holder is a zero-height sticky strip at the top
   of the page, so the menu stays on screen in the export and inside the editor's scaled Preview
   alike. `initSectionNav` sets `--cap-nav-viewport` (the visible height, in the page's own px) and
   `--cap-nav-edge` (the distance from the page edge out to the window edge) so it centres on, and
   hugs the edge of, the real window. `.is-invert` uses difference blending so one colour reads
   over light and dark sections; that blend has to sit on the holder, the stacking context that
   meets the sections. */
.cap-nav-holder { position: sticky; top: 0; height: 0; z-index: 70; pointer-events: none; }
.cap-nav-holder.is-invert { mix-blend-mode: difference; }
.cap-nav {
  position: absolute; top: calc(var(--cap-nav-viewport, 100vh) / 2); transform: translateY(-50%);
  pointer-events: auto; color: var(--cap-nav-color, currentColor);
  font-family: var(--cap-font-body); font-size: var(--cap-nav-font, 11px); line-height: 1.2; letter-spacing: .02em;
}
.cap-nav--left { left: calc(var(--cap-nav-offset, 32px) + var(--cap-nav-edge, 0px)); }
.cap-nav--right { right: calc(var(--cap-nav-offset, 32px) + var(--cap-nav-edge, 0px)); text-align: right; }
.cap-nav__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--cap-nav-gap, 14px); }
.cap-nav--right .cap-nav__list { align-items: flex-end; }
.cap-nav__item {
  display: flex; align-items: center; gap: .9em; color: inherit; text-decoration: none; cursor: pointer;
  opacity: var(--cap-nav-opacity, .4); transition: opacity calc(.4s * var(--cap-pace, 1)) var(--cap-ease, cubic-bezier(.165, .84, .44, 1)), color calc(.4s * var(--cap-pace, 1)) var(--cap-ease, cubic-bezier(.165, .84, .44, 1));
}
.cap-nav--right .cap-nav__item { flex-direction: row-reverse; }
.cap-nav__item:hover, .cap-nav__item:focus-visible, .cap-nav__item.is-active { opacity: 1; }
.cap-nav__item.is-active { color: var(--cap-nav-active, currentColor); }
.cap-nav__item:focus-visible { outline: 1px solid currentColor; outline-offset: 4px; }
/* The current section's dot draws out into a short bar. */
.cap-nav__dot {
  flex: none; width: var(--cap-nav-dot, 8px); height: var(--cap-nav-dot, 8px); border-radius: var(--cap-nav-dot, 8px);
  background: currentColor; transform: scale(.7);
  transition: width calc(.5s * var(--cap-pace, 1)) var(--cap-ease, cubic-bezier(.165, .84, .44, 1)), transform calc(.4s * var(--cap-pace, 1)) var(--cap-ease, cubic-bezier(.165, .84, .44, 1));
}
.cap-nav__item:hover .cap-nav__dot { transform: scale(1); }
.cap-nav__item.is-active .cap-nav__dot { width: calc(var(--cap-nav-dot, 8px) * 3); transform: scale(1); }
.cap-nav__label { white-space: nowrap; transition: opacity calc(.4s * var(--cap-pace, 1)) var(--cap-ease, cubic-bezier(.165, .84, .44, 1)), transform calc(.4s * var(--cap-pace, 1)) var(--cap-ease, cubic-bezier(.165, .84, .44, 1)); }
/* Dots only: a label slides out beside its dot on hover or keyboard focus. */
.cap-nav--dots .cap-nav__label,
.cap-nav--dotsLabels.cap-nav--labels-active .cap-nav__item:not(.is-active) .cap-nav__label {
  opacity: 0; transform: translateX(-.5em); pointer-events: none;
}
.cap-nav--right.cap-nav--dots .cap-nav__label,
.cap-nav--right.cap-nav--dotsLabels.cap-nav--labels-active .cap-nav__item:not(.is-active) .cap-nav__label { transform: translateX(.5em); }
.cap-nav--dots .cap-nav__item:hover .cap-nav__label,
.cap-nav--dots .cap-nav__item:focus-visible .cap-nav__label,
.cap-nav--dotsLabels.cap-nav--labels-active .cap-nav__item:hover .cap-nav__label,
.cap-nav--dotsLabels.cap-nav--labels-active .cap-nav__item:focus-visible .cap-nav__label { opacity: 1; transform: none; }
@media print { .cap-nav-holder, .cap-ripple-canvas { display: none; } }

/* Picture water ripple (`SectionLayer.imageRipple`): the rippling copy is a canvas placed exactly
   over its picture by `initImageRipple`, copying the picture's framing, grade, opacity and corners.
   The picture hides under it only while the water moves. */
.cap-image-ripple { position: absolute; display: block; max-width: none; pointer-events: none; }
.cap-image-ripple-under { opacity: 0 !important; }

/* Pointer water ripples (`CanvasConfig.cursorRipple`). `.cap-ripple` only carries the settings;
   `initCursorRipple` draws into its own canvas on <body>, outside the page's fit-to-window scale. */
.cap-ripple { display: none; }
.cap-ripple-canvas { position: fixed; pointer-events: none; z-index: 60; }

/* Custom cursor (Phase 3, dynamism brief) — a project-wide, off-by-default replacement for the
   pointer. `initCustomCursor` (effects.ts) moves this via a GPU-cheap transform and toggles
   `.is-hover`; nothing here needs to know the visitor's device — that's this block's own job via
   the fallback rules below, belt-and-braces alongside the runtime's own `(pointer: coarse)` check. */
.cap-cursor {
  position: fixed;
  top: 0; left: 0;
  z-index: 9999;
  pointer-events: none;
  will-change: transform;
  transform: translate3d(-100vw, -100vh, 0);
  /* No transition on the position transform — the runtime sets it every pointermove, so a
     transition just makes the cursor trail (lag) behind the real pointer. The shape's own
     hover-scale transition below stays. */
  color: var(--cap-cursor-color, currentColor);
}
.cap-cursor__shape { display: block; }
/* Dot / ring / dot+ring: a plain circle, sized off one shared var so a style swap is a one-line
   change. `dotRing` layers a larger, softer outline behind the same solid dot. */
.cap-cursor--dot .cap-cursor__shape, .cap-cursor--ring .cap-cursor__shape, .cap-cursor--dotRing .cap-cursor__shape {
  position: absolute; top: 50%; left: 50%;
  width: var(--cap-cursor-size, 16px); height: var(--cap-cursor-size, 16px);
  margin: calc(var(--cap-cursor-size, 16px) / -2) 0 0 calc(var(--cap-cursor-size, 16px) / -2);
  border-radius: 50%;
  background: currentColor;
  transition: transform .18s ease-out, opacity .18s ease-out;
}
.cap-cursor--ring .cap-cursor__shape { background: transparent; border: 2px solid currentColor; }
.cap-cursor--dotRing .cap-cursor__shape { box-shadow: 0 0 0 6px color-mix(in srgb, currentColor 25%, transparent); }
.cap-cursor--dot.is-hover .cap-cursor__shape,
.cap-cursor--ring.is-hover .cap-cursor__shape,
.cap-cursor--dotRing.is-hover .cap-cursor__shape { transform: scale(2.5); }
/* Arrow / arrow outline: a small redrawn pointer shape, the more contemporary alternative. */
.cap-cursor--arrow .cap-cursor__shape, .cap-cursor--arrowOutline .cap-cursor__shape {
  position: absolute; top: 0; left: 0;
  width: calc(var(--cap-cursor-size, 16px) * 1.5); height: calc(var(--cap-cursor-size, 16px) * 1.5);
  fill: currentColor; stroke: currentColor; stroke-width: 0;
  transition: transform .18s ease-out;
}
.cap-cursor--arrowOutline .cap-cursor__shape { fill: none; stroke-width: 1.5; }
.cap-cursor--arrow.is-hover .cap-cursor__shape, .cap-cursor--arrowOutline.is-hover .cap-cursor__shape { transform: scale(1.6); }
/* The native cursor only ever disappears once the runtime has confirmed a desktop, fine pointer
   is actually driving the replacement — never a bare "hidden with nothing to replace it" state. */
.cap-page.cap-cursor-active, .cap-page.cap-cursor-active a, .cap-page.cap-cursor-active button { cursor: none; }
@media (pointer: coarse), (hover: none) {
  .cap-cursor { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .cap-page *, .cap-page *::before, .cap-page *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; scroll-behavior: auto !important; }
  .cap-parallax-media { transform: none !important; }
  .cap-cursor { display: none; }
}
/* Colour-grade filter definitions. Present only to be referenced, never to be seen. */
.cap-grade-defs { position: absolute; width: 0; height: 0; overflow: hidden; pointer-events: none; }

/* Marquee band: editor chrome, only visible while sweeping. Deliberately not `.cap-marquee`,
   which is the Marquee gallery strip — sharing the name pulled every strip out of flow. */
.cap-select-band { position: absolute; z-index: 45; pointer-events: none; border: calc(1px * var(--cap-handle-scale, 1)) solid var(--cap-accent); background: color-mix(in srgb, var(--cap-accent) 14%, transparent); }

/* Gallery lightbox. Lives on <body>, above everything, so no section's overflow clips it. */
.cap-lightbox { position: fixed; inset: 0; z-index: 200; display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: clamp(.5rem, 2vw, 2rem); padding: clamp(1rem, 4vw, 3rem); }
.cap-lightbox[hidden] { display: none; }
/* Dark enough that light page text underneath stops competing with the picture. */
.cap-lightbox__backdrop { position: absolute; inset: 0; background: rgba(8, 10, 9, .97); backdrop-filter: blur(6px); cursor: zoom-out; }
.cap-lightbox__stage { position: relative; margin: 0; display: grid; justify-items: center; gap: 1rem; min-width: 0; max-height: 100%; }
.cap-lightbox__image { display: block; max-width: 100%; max-height: 82vh; width: auto; height: auto; object-fit: contain; background: #1a1e1c; }
.cap-lightbox__caption { color: rgba(255,255,255,.86); font-family: var(--cap-font-caption, var(--cap-font-body)); font-size: .84rem; letter-spacing: .08em; text-transform: uppercase; text-align: center; }
.cap-lightbox__caption[hidden] { display: none; }
.cap-lightbox__nav, .cap-lightbox__close { position: relative; z-index: 1; display: grid; place-items: center; padding: 0; color: #fff; background: rgba(255,255,255,.1); border: 1px solid rgba(255,255,255,.25); cursor: pointer; font: 400 1.9rem/1 Arial, sans-serif; transition: background-color .18s ease; }
.cap-lightbox__nav { width: clamp(2.4rem, 5vw, 3.4rem); height: clamp(2.4rem, 5vw, 3.4rem); border-radius: 50%; }
.cap-lightbox__nav[hidden] { visibility: hidden; display: grid; }
.cap-lightbox__nav:hover, .cap-lightbox__close:hover { background: rgba(255,255,255,.24); }
.cap-lightbox__close { position: absolute; top: clamp(.75rem, 2vw, 1.5rem); right: clamp(.75rem, 2vw, 1.5rem); width: 2.6rem; height: 2.6rem; border-radius: 50%; font-size: 1.6rem; }
.cap-lightbox__nav:focus-visible, .cap-lightbox__close:focus-visible { outline: 2px solid var(--cap-accent, #d8ff45); outline-offset: 3px; }
/* The page must not scroll behind the viewer. */
body.cap-lightbox-open { overflow: hidden; }
[data-lightbox-src] { cursor: zoom-in; }
@media (prefers-reduced-motion: reduce) { .cap-lightbox__nav, .cap-lightbox__close { transition: none; } }
.cap-print-page { position: relative; flex: none; overflow: hidden; }
.cap-print-page > .cap-page { min-height: 0; height: 100%; }
.cap-print-page .cap-section-frame { margin: 0; }

@font-face { font-family: "Lyon Text"; src: url("./assets/fc6eb0174068faf3.otf"); font-weight: 900; font-style: normal; font-stretch: normal; font-display: swap; }
@font-face { font-family: "Lyon Text"; src: url("./assets/629556553b6a88c0.otf"); font-weight: 900; font-style: italic; font-stretch: normal; font-display: swap; }
@font-face { font-family: "Lyon Text"; src: url("./assets/5e9370b2b344fc20.otf"); font-weight: 700; font-style: normal; font-stretch: normal; font-display: swap; }
@font-face { font-family: "Lyon Text"; src: url("./assets/acafc66cec15af3a.otf"); font-weight: 700; font-style: italic; font-stretch: normal; font-display: swap; }
@font-face { font-family: "Lyon Text"; src: url("./assets/4f6c00ea8b8ceddf.otf"); font-weight: 400; font-style: normal; font-stretch: normal; font-display: swap; }
@font-face { font-family: "Lyon Text"; src: url("./assets/4c76a03a9de6316b.otf"); font-weight: 400; font-style: italic; font-stretch: normal; font-display: swap; }
@font-face { font-family: "Lyon Text"; src: url("./assets/64f77afaa0debe0f.otf"); font-weight: 450; font-style: normal; font-stretch: normal; font-display: swap; }
@font-face { font-family: "Lyon Text"; src: url("./assets/db29d2ef43f0dfcf.otf"); font-weight: 450; font-style: italic; font-stretch: normal; font-display: swap; }
@font-face { font-family: "Lyon Text"; src: url("./assets/17a9bd286044fdae.otf"); font-weight: 600; font-style: normal; font-stretch: normal; font-display: swap; }
@font-face { font-family: "Lyon Text"; src: url("./assets/bd501c2e1ef5ee04.otf"); font-weight: 600; font-style: italic; font-stretch: normal; font-display: swap; }
@font-face { font-family: "GT Walsheim"; src: url("./assets/f0d3ce0e758561c0.otf"); font-weight: 800; font-style: normal; font-stretch: normal; font-display: swap; }
@font-face { font-family: "GT Walsheim"; src: url("./assets/63e0cf415039f4eb.otf"); font-weight: 800; font-style: oblique; font-stretch: normal; font-display: swap; }
@font-face { font-family: "GT Walsheim"; src: url("./assets/5b9cb8bc6352f4f0.otf"); font-weight: 700; font-style: normal; font-stretch: normal; font-display: swap; }
@font-face { font-family: "GT Walsheim"; src: url("./assets/a9f02726de2a8961.otf"); font-weight: 700; font-style: oblique; font-stretch: normal; font-display: swap; }
@font-face { font-family: "GT Walsheim"; src: url("./assets/ace997f6ff30797b.otf"); font-weight: 350; font-style: normal; font-stretch: normal; font-display: swap; }
@font-face { font-family: "GT Walsheim"; src: url("./assets/7e5e70b852426e4d.otf"); font-weight: 350; font-style: oblique; font-stretch: normal; font-display: swap; }
@font-face { font-family: "GT Walsheim"; src: url("./assets/2ad2b9bfa81d620f.otf"); font-weight: 600; font-style: normal; font-stretch: normal; font-display: swap; }
@font-face { font-family: "GT Walsheim"; src: url("./assets/fbab2e3eba7016e2.otf"); font-weight: 600; font-style: oblique; font-stretch: normal; font-display: swap; }
@font-face { font-family: "GT Walsheim"; src: url("./assets/882bf1390dcf3b39.otf"); font-weight: 400; font-style: normal; font-stretch: normal; font-display: swap; }
@font-face { font-family: "GT Walsheim"; src: url("./assets/08c83fbda4068cbc.otf"); font-weight: 400; font-style: oblique; font-stretch: normal; font-display: swap; }
@font-face { font-family: "GT Walsheim"; src: url("./assets/b6d40e1f1afdaacb.otf"); font-weight: 300; font-style: normal; font-stretch: normal; font-display: swap; }
@font-face { font-family: "GT Walsheim"; src: url("./assets/dc795454d03f2426.otf"); font-weight: 300; font-style: oblique; font-stretch: normal; font-display: swap; }
@font-face { font-family: "GT Walsheim"; src: url("./assets/1b2e2204dad0063f.otf"); font-weight: 900; font-style: normal; font-stretch: normal; font-display: swap; }
@font-face { font-family: "GT Walsheim"; src: url("./assets/8da2abac0a341dc9.otf"); font-weight: 900; font-style: oblique; font-stretch: normal; font-display: swap; }
@font-face { font-family: "GT Walsheim"; src: url("./assets/9fca51f8b1133e88.otf"); font-weight: 250; font-style: normal; font-stretch: normal; font-display: swap; }
@font-face { font-family: "GT Walsheim"; src: url("./assets/f57b57464f3eaf95.otf"); font-weight: 250; font-style: oblique; font-stretch: normal; font-display: swap; }
:root {
  --cap-ink: #000000;
  --cap-paper: #E7E2DE;
  --cap-accent: #73897C;
  --cap-muted: #878787;
  --cap-white: #F9F9F9;
  --cap-charcoal: #535349;
  --cap-font-heading: "Lyon Text";
  --cap-font-body: "GT Walsheim";
  --cap-font-small-heading: "GT Walsheim";
  --cap-font-accent: "GT Walsheim";
  --cap-font-caption: "GT Walsheim";
  --cap-style-large-heading-font-family: var(--cap-font-heading);
  --cap-style-large-heading-font-weight: 300;
  --cap-style-large-heading-line-height: 1;
  --cap-style-large-heading-letter-spacing: 0px;
  --cap-style-large-heading-text-transform: none;
  --cap-style-large-heading-color: #F9F9F9;
  --cap-style-large-heading-font-style: italic;
  --cap-style-large-heading-text-decoration: none;
  --cap-style-large-heading-font-size: 112px;
  --cap-style-body-font-family: var(--cap-font-body);
  --cap-style-body-font-weight: 400;
  --cap-style-body-line-height: 1.5;
  --cap-style-body-letter-spacing: 0px;
  --cap-style-body-text-transform: none;
  --cap-style-body-color: #000000;
  --cap-style-body-font-style: normal;
  --cap-style-body-text-decoration: none;
  --cap-style-body-font-size: 18px;
  --cap-style-caption-font-family: var(--cap-font-heading);
  --cap-style-caption-font-weight: 300;
  --cap-style-caption-line-height: 2;
  --cap-style-caption-letter-spacing: 0px;
  --cap-style-caption-text-transform: none;
  --cap-style-caption-color: #ACA094;
  --cap-style-caption-font-style: italic;
  --cap-style-caption-text-decoration: none;
  --cap-style-caption-font-size: 14px;
  --cap-style-57a35a1b-a9ed-4cf5-85ef-7d9484ab1144-font-family: var(--cap-font-heading);
  --cap-style-57a35a1b-a9ed-4cf5-85ef-7d9484ab1144-font-weight: 500;
  --cap-style-57a35a1b-a9ed-4cf5-85ef-7d9484ab1144-line-height: 1;
  --cap-style-57a35a1b-a9ed-4cf5-85ef-7d9484ab1144-letter-spacing: -2px;
  --cap-style-57a35a1b-a9ed-4cf5-85ef-7d9484ab1144-text-transform: none;
  --cap-style-57a35a1b-a9ed-4cf5-85ef-7d9484ab1144-color: #F9F9F9;
  --cap-style-57a35a1b-a9ed-4cf5-85ef-7d9484ab1144-font-style: normal;
  --cap-style-57a35a1b-a9ed-4cf5-85ef-7d9484ab1144-text-decoration: none;
  --cap-style-57a35a1b-a9ed-4cf5-85ef-7d9484ab1144-font-size: 112px;
  --cap-style-8d010de2-1b3c-4b35-b830-d5b3744e04ca-font-family: var(--cap-font-heading);
  --cap-style-8d010de2-1b3c-4b35-b830-d5b3744e04ca-font-weight: 500;
  --cap-style-8d010de2-1b3c-4b35-b830-d5b3744e04ca-line-height: 1;
  --cap-style-8d010de2-1b3c-4b35-b830-d5b3744e04ca-letter-spacing: -2px;
  --cap-style-8d010de2-1b3c-4b35-b830-d5b3744e04ca-text-transform: none;
  --cap-style-8d010de2-1b3c-4b35-b830-d5b3744e04ca-color: #F9F9F9;
  --cap-style-8d010de2-1b3c-4b35-b830-d5b3744e04ca-font-style: normal;
  --cap-style-8d010de2-1b3c-4b35-b830-d5b3744e04ca-text-decoration: none;
  --cap-style-8d010de2-1b3c-4b35-b830-d5b3744e04ca-font-size: 130px;
  --cap-style-604d6351-f0a7-4be9-9497-5c3e3526265f-font-family: var(--cap-font-body);
  --cap-style-604d6351-f0a7-4be9-9497-5c3e3526265f-font-weight: 400;
  --cap-style-604d6351-f0a7-4be9-9497-5c3e3526265f-line-height: 1.5;
  --cap-style-604d6351-f0a7-4be9-9497-5c3e3526265f-letter-spacing: 0px;
  --cap-style-604d6351-f0a7-4be9-9497-5c3e3526265f-text-transform: none;
  --cap-style-604d6351-f0a7-4be9-9497-5c3e3526265f-color: #F9F9F9;
  --cap-style-604d6351-f0a7-4be9-9497-5c3e3526265f-font-style: normal;
  --cap-style-604d6351-f0a7-4be9-9497-5c3e3526265f-text-decoration: none;
  --cap-style-604d6351-f0a7-4be9-9497-5c3e3526265f-font-size: 22px;
  --cap-style-ac404fb3-f2b5-49ae-b04b-60d3e2290b2d-font-family: var(--cap-font-accent);
  --cap-style-ac404fb3-f2b5-49ae-b04b-60d3e2290b2d-font-weight: 500;
  --cap-style-ac404fb3-f2b5-49ae-b04b-60d3e2290b2d-line-height: 1.1;
  --cap-style-ac404fb3-f2b5-49ae-b04b-60d3e2290b2d-letter-spacing: 0px;
  --cap-style-ac404fb3-f2b5-49ae-b04b-60d3e2290b2d-text-transform: none;
  --cap-style-ac404fb3-f2b5-49ae-b04b-60d3e2290b2d-color: #ffffff;
  --cap-style-ac404fb3-f2b5-49ae-b04b-60d3e2290b2d-font-style: normal;
  --cap-style-ac404fb3-f2b5-49ae-b04b-60d3e2290b2d-text-decoration: none;
  --cap-style-ac404fb3-f2b5-49ae-b04b-60d3e2290b2d-font-size: 98px;
  --cap-style-06c65595-e0f7-40fb-a9ab-97416c7d5597-font-family: var(--cap-font-small-heading);
  --cap-style-06c65595-e0f7-40fb-a9ab-97416c7d5597-font-weight: 500;
  --cap-style-06c65595-e0f7-40fb-a9ab-97416c7d5597-line-height: 1.5;
  --cap-style-06c65595-e0f7-40fb-a9ab-97416c7d5597-letter-spacing: 0px;
  --cap-style-06c65595-e0f7-40fb-a9ab-97416c7d5597-text-transform: uppercase;
  --cap-style-06c65595-e0f7-40fb-a9ab-97416c7d5597-color: #ffffff;
  --cap-style-06c65595-e0f7-40fb-a9ab-97416c7d5597-font-style: normal;
  --cap-style-06c65595-e0f7-40fb-a9ab-97416c7d5597-text-decoration: none;
  --cap-style-06c65595-e0f7-40fb-a9ab-97416c7d5597-font-size: 15px;
  --cap-style-9e83ac79-7559-4ae7-bf25-859d6107f45e-font-family: var(--cap-font-body);
  --cap-style-9e83ac79-7559-4ae7-bf25-859d6107f45e-font-weight: 300;
  --cap-style-9e83ac79-7559-4ae7-bf25-859d6107f45e-line-height: 1.2;
  --cap-style-9e83ac79-7559-4ae7-bf25-859d6107f45e-letter-spacing: 0px;
  --cap-style-9e83ac79-7559-4ae7-bf25-859d6107f45e-text-transform: none;
  --cap-style-9e83ac79-7559-4ae7-bf25-859d6107f45e-color: #000000;
  --cap-style-9e83ac79-7559-4ae7-bf25-859d6107f45e-font-style: normal;
  --cap-style-9e83ac79-7559-4ae7-bf25-859d6107f45e-text-decoration: none;
  --cap-style-9e83ac79-7559-4ae7-bf25-859d6107f45e-font-size: 25px;
  --cap-style-2946b97a-b0ed-4ccf-b6e5-571f31af4e2e-font-family: var(--cap-font-heading);
  --cap-style-2946b97a-b0ed-4ccf-b6e5-571f31af4e2e-font-weight: 300;
  --cap-style-2946b97a-b0ed-4ccf-b6e5-571f31af4e2e-line-height: 2;
  --cap-style-2946b97a-b0ed-4ccf-b6e5-571f31af4e2e-letter-spacing: 0px;
  --cap-style-2946b97a-b0ed-4ccf-b6e5-571f31af4e2e-text-transform: none;
  --cap-style-2946b97a-b0ed-4ccf-b6e5-571f31af4e2e-color: #ACA094;
  --cap-style-2946b97a-b0ed-4ccf-b6e5-571f31af4e2e-font-style: italic;
  --cap-style-2946b97a-b0ed-4ccf-b6e5-571f31af4e2e-text-decoration: none;
  --cap-style-2946b97a-b0ed-4ccf-b6e5-571f31af4e2e-font-size: 21px;
  --cap-style-e1c70904-aaf2-4a3f-87ea-cbc76941480d-font-family: var(--cap-font-body);
  --cap-style-e1c70904-aaf2-4a3f-87ea-cbc76941480d-font-weight: 300;
  --cap-style-e1c70904-aaf2-4a3f-87ea-cbc76941480d-line-height: 1.2;
  --cap-style-e1c70904-aaf2-4a3f-87ea-cbc76941480d-letter-spacing: 0px;
  --cap-style-e1c70904-aaf2-4a3f-87ea-cbc76941480d-text-transform: none;
  --cap-style-e1c70904-aaf2-4a3f-87ea-cbc76941480d-color: #000000;
  --cap-style-e1c70904-aaf2-4a3f-87ea-cbc76941480d-font-style: normal;
  --cap-style-e1c70904-aaf2-4a3f-87ea-cbc76941480d-text-decoration: none;
  --cap-style-e1c70904-aaf2-4a3f-87ea-cbc76941480d-font-size: 21px;
  --cap-style-bf72e9c1-73ae-438a-85c6-e299e55e8156-font-family: var(--cap-font-body);
  --cap-style-bf72e9c1-73ae-438a-85c6-e299e55e8156-font-weight: 300;
  --cap-style-bf72e9c1-73ae-438a-85c6-e299e55e8156-line-height: 1.2;
  --cap-style-bf72e9c1-73ae-438a-85c6-e299e55e8156-letter-spacing: 0px;
  --cap-style-bf72e9c1-73ae-438a-85c6-e299e55e8156-text-transform: none;
  --cap-style-bf72e9c1-73ae-438a-85c6-e299e55e8156-color: #000000;
  --cap-style-bf72e9c1-73ae-438a-85c6-e299e55e8156-font-style: normal;
  --cap-style-bf72e9c1-73ae-438a-85c6-e299e55e8156-text-decoration: none;
  --cap-style-bf72e9c1-73ae-438a-85c6-e299e55e8156-font-size: 14px;
  --cap-style-f71b465a-299b-4849-9ea2-d4ea0d967573-font-family: var(--cap-font-body);
  --cap-style-f71b465a-299b-4849-9ea2-d4ea0d967573-font-weight: 300;
  --cap-style-f71b465a-299b-4849-9ea2-d4ea0d967573-line-height: 1.2;
  --cap-style-f71b465a-299b-4849-9ea2-d4ea0d967573-letter-spacing: 0px;
  --cap-style-f71b465a-299b-4849-9ea2-d4ea0d967573-text-transform: none;
  --cap-style-f71b465a-299b-4849-9ea2-d4ea0d967573-color: #878787;
  --cap-style-f71b465a-299b-4849-9ea2-d4ea0d967573-font-style: normal;
  --cap-style-f71b465a-299b-4849-9ea2-d4ea0d967573-text-decoration: none;
  --cap-style-f71b465a-299b-4849-9ea2-d4ea0d967573-font-size: 14px;
  --cap-style-e81ac7d5-c1d8-4d3f-ae05-d5ca3e7a2e24-font-family: var(--cap-font-body);
  --cap-style-e81ac7d5-c1d8-4d3f-ae05-d5ca3e7a2e24-font-weight: 300;
  --cap-style-e81ac7d5-c1d8-4d3f-ae05-d5ca3e7a2e24-line-height: 1.2;
  --cap-style-e81ac7d5-c1d8-4d3f-ae05-d5ca3e7a2e24-letter-spacing: 0px;
  --cap-style-e81ac7d5-c1d8-4d3f-ae05-d5ca3e7a2e24-text-transform: none;
  --cap-style-e81ac7d5-c1d8-4d3f-ae05-d5ca3e7a2e24-color: #000000;
  --cap-style-e81ac7d5-c1d8-4d3f-ae05-d5ca3e7a2e24-font-style: normal;
  --cap-style-e81ac7d5-c1d8-4d3f-ae05-d5ca3e7a2e24-text-decoration: none;
  --cap-style-e81ac7d5-c1d8-4d3f-ae05-d5ca3e7a2e24-font-size: 14px;
  --cap-style-d20e37ad-7edf-4624-8b19-05095e04540d-font-family: var(--cap-font-heading);
  --cap-style-d20e37ad-7edf-4624-8b19-05095e04540d-font-weight: 300;
  --cap-style-d20e37ad-7edf-4624-8b19-05095e04540d-line-height: 1.25;
  --cap-style-d20e37ad-7edf-4624-8b19-05095e04540d-letter-spacing: 0px;
  --cap-style-d20e37ad-7edf-4624-8b19-05095e04540d-text-transform: none;
  --cap-style-d20e37ad-7edf-4624-8b19-05095e04540d-color: #ACA094;
  --cap-style-d20e37ad-7edf-4624-8b19-05095e04540d-font-style: italic;
  --cap-style-d20e37ad-7edf-4624-8b19-05095e04540d-text-decoration: none;
  --cap-style-d20e37ad-7edf-4624-8b19-05095e04540d-font-size: 16px;
  --cap-style-d169a1ef-aac8-420d-b792-f06065834c2b-font-family: var(--cap-font-heading);
  --cap-style-d169a1ef-aac8-420d-b792-f06065834c2b-font-weight: 300;
  --cap-style-d169a1ef-aac8-420d-b792-f06065834c2b-line-height: 1.15;
  --cap-style-d169a1ef-aac8-420d-b792-f06065834c2b-letter-spacing: 0px;
  --cap-style-d169a1ef-aac8-420d-b792-f06065834c2b-text-transform: none;
  --cap-style-d169a1ef-aac8-420d-b792-f06065834c2b-color: #535349;
  --cap-style-d169a1ef-aac8-420d-b792-f06065834c2b-font-style: italic;
  --cap-style-d169a1ef-aac8-420d-b792-f06065834c2b-text-decoration: none;
  --cap-style-d169a1ef-aac8-420d-b792-f06065834c2b-font-size: 35px;
}
.cap-page{min-height:100vh}
.cap-page--card{min-height:calc(100vh - 8vmin);margin-block:4vmin}

/* Generated from this project's canvas presets and per-breakpoint placements. */
body{margin:0;overflow-x:hidden;background:#ffffff}
.cap-layout{display:none}
.cap-layout--mobile{display:block}
.cap-layout--mobile .cap-page{width:420px;margin-inline:auto}
.cap-layout--mobile{zoom:min(1, calc(100vw / 420px))}
@media (min-width:768px){
.cap-layout--mobile{display:none}
.cap-layout--desktop{display:block}
.cap-layout--desktop .cap-page{width:1920px;margin-inline:auto}
.cap-layout--desktop{zoom:min(1, calc(100vw / 1920px))}
}
