/* Homepage top projects (index.html): the Design System card's component
   board and the Page Templates wireframe deck. The shared card styles (.cs-tldr--home,
   .cs-tldr__visual) live in style.css; this file only adds what these two
   cards need. Loaded by index.html only. */

#project-design-system .cs-tldr--home {
    --homeAccent: #7122ec;
}

/* Appway and FNZ share the brand row. */
.cs-tldr--home .cs-tldr__header .topLogo--fnz {
    max-height: 22px;
}

/* --- Design System: component definitions on an isometric board -----------
   Redlined specs from the design system docs (each part of a component
   numbered), laid out as frames on a plane tilted like the case-study
   illustration. The plane stays flat (no preserve-3d: Chrome hit-tests cards
   in a 3D context unreliably, and they need to take clicks), so a hovered
   card "lifts" by sliding along the plane's diagonal that points straight up
   on screen, (1, -1), leaving its shadow on the board. As the card scrolls through the viewport the rows slide
   past each other (scroll-driven, no script); elsewhere they sit still.
   The rows are centred and grow towards the middle, a diamond on the plane
   that projects to a rectangle covering the whole card. Clicking a spec opens
   it full size (ds-specs.js).
   Weight: the board is one 35 KB sprite (8 × 5 cells of 256 × 160, padding
   baked in), requested only once the card nears the viewport (.is-near, set
   by ds-specs.js). Full-size specs load one at a time, in the viewer. */
.dsSpecs {
    --ds-card-w: 21cqw;
    --ds-ease: cubic-bezier(0.22, 1, 0.36, 1);
    position: relative;
    aspect-ratio: 16 / 10;
    overflow: hidden;
    container-type: inline-size;
    view-timeline: --dsSpecs block;
    line-height: 1.2;
    background:
        radial-gradient(circle, color-mix(in srgb, var(--homeAccent) 26%, transparent) 1px, transparent 1.5px) 0 0 / 20px 20px,
        linear-gradient(170deg, color-mix(in srgb, var(--homeAccent) 4%, #fff), color-mix(in srgb, var(--homeAccent) 14%, #fff));
}

.dsSpecs__plane {
    position: absolute;
    top: 50%;
    left: 50%;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3.4cqw;
    transform: translate(-50%, -50%) rotateX(56deg) rotateZ(-45deg);
    transition: transform 1.1s var(--ds-ease);
}

.topProject:hover .dsSpecs__plane {
    transform: translate(-50%, -50%) rotateX(50deg) rotateZ(-40deg) scale(1.05);
}

.dsSpecs__row {
    display: flex;
    gap: 2.4cqw;
    width: max-content;
}

.dsSpecs__row:hover {
    z-index: 1;
}

.dsSpecs__card {
    flex: none;
    width: var(--ds-card-w);
    margin: 0;
    padding: 0;
    transition: transform 0.5s var(--ds-ease);
    cursor: zoom-in;
}

/* The hovered frame rises off the board, name and all. */
.dsSpecs__card:hover {
    z-index: 1;
    transform: translate(1.4cqw, -1.4cqw) scale(1.1);
}

/* Frame name, the way Figma labels a frame above its corner. */
.dsSpecs__card figcaption {
    margin: 0 0 0.7cqw;
    padding: 0;
    overflow: hidden;
    color: color-mix(in srgb, var(--homeAccent) 75%, #050038);
    font-family: var(--font-meta, ui-monospace, monospace);
    font-size: max(8px, 1.25cqw);
    line-height: 1.2;
    text-align: left;
    white-space: nowrap;
    text-overflow: ellipsis;
}

.dsSpecs__frame {
    aspect-ratio: 16 / 10;
    overflow: hidden;
    background-color: #fff;
    background-repeat: no-repeat;
    background-size: 800% 500%;
    background-position: calc(var(--c) / 7 * 100%) calc(var(--r) / 4 * 100%);
    border-radius: 0.8cqw;
    box-shadow:
        0 0 0 1px color-mix(in srgb, var(--homeAccent) 16%, transparent),
        0.4cqw 0.6cqw 0 color-mix(in srgb, var(--homeAccent) 10%, transparent);
    transition: box-shadow 0.5s var(--ds-ease);
}

.dsSpecs.is-near .dsSpecs__frame {
    background-image: url("../../work/media/DesignSystem/definitions/sprite.webp");
}

.dsSpecs__card:hover .dsSpecs__frame {
    box-shadow:
        0 0 0 1.5px var(--homeAccent),
        -1.8cqw 1.8cqw 1.6cqw -0.4cqw color-mix(in srgb, var(--homeAccent) 38%, transparent);
}

/* The keyboard way in: the site's "See all" pill, on the board's corner. */
.dsSpecs__actions {
    position: absolute;
    left: 14px;
    bottom: 14px;
    z-index: 2;
}

.dsSpecs:not(.is-ready) .dsSpecs__actions {
    display: none;
}

.dsSpecs .cs-carousel__see-all {
    border-color: var(--frgd) !important;
}

@supports (animation-timeline: view()) {
    @media (prefers-reduced-motion: no-preference) {
        .dsSpecs__row {
            animation: dsSpecsDrift linear both;
            animation-timeline: --dsSpecs;
            animation-range: cover 0% cover 100%;
        }

        .dsSpecs__row:nth-child(even) {
            animation-name: dsSpecsDriftBack;
        }
    }
}

@keyframes dsSpecsDrift {
    from { transform: translateX(7cqw); }
    to   { transform: translateX(-7cqw); }
}

@keyframes dsSpecsDriftBack {
    from { transform: translateX(-7cqw); }
    to   { transform: translateX(7cqw); }
}

@media (prefers-reduced-motion: reduce) {
    .dsSpecs__plane,
    .dsSpecs__card,
    .dsSpecs__frame {
        transition: none;
    }

    .topProject:hover .dsSpecs__plane {
        transform: translate(-50%, -50%) rotateX(56deg) rotateZ(-45deg);
    }
}

/* --- Page Templates: a cover-flow of wireframes ---------------------------
   The front sheet scrolls its page top to bottom like a browser tab; when it
   reaches the footer, wireframe-deck.js moves the deck on. Sheets are sized in
   cqw of the deck, so the whole composition scales with the card.
   data-pos is the sheet's distance from the front (-3…3, or "far").
   Weight: every sheet is first painted from one 51 KB strip sprite (all
   twelve pages at 180 px wide), requested once the deck nears the viewport
   (.is-near, wireframe-deck.js). Only the front page and the next one get
   their full image, which fades in over the sprite. */
.wfDeck {
    --wf-sheet-w: 31cqw;
    --wf-sheet-h: 46cqw;
    --wf-dur: 6s;
    --wf-ease: cubic-bezier(0.22, 1, 0.36, 1);
    --wf-ink: var(--frgd, #050038);
    position: relative;
    container-type: inline-size;
    line-height: 1.4;
    background:
        radial-gradient(circle, color-mix(in srgb, var(--homeAccent) 24%, transparent) 1px, transparent 1.5px) 0 0 / 20px 20px,
        linear-gradient(180deg, color-mix(in srgb, var(--homeAccent) 3%, #fff), color-mix(in srgb, var(--homeAccent) 10%, #fff));
}

.wfDeck__stage {
    position: relative;
    aspect-ratio: 16 / 9;
    overflow: hidden;
    perspective: 1600px;
    touch-action: pan-y;
}

.wfDeck__slides {
    position: absolute;
    inset: 0;
    margin: 0;
    padding: 0;
    list-style: none;
    transform-style: preserve-3d;
}

.wfDeck__slide {
    --x: 0;
    --s: 1;
    --r: 0deg;
    position: absolute;
    top: 50%;
    left: 50%;
    width: var(--wf-sheet-w);
    height: var(--wf-sheet-h);
    margin: 0;
    padding: 0;
    list-style: none;
    z-index: 1;
    opacity: 0;
    visibility: hidden;
    transform:
        translate(-50%, -50%)
        translateX(calc(var(--x) * var(--wf-sheet-w)))
        rotateY(var(--r))
        scale(var(--s));
    transition:
        transform 0.75s var(--wf-ease),
        opacity 0.45s ease,
        visibility 0s linear 0.45s;
    cursor: pointer;
}

.wfDeck__slide[data-pos="0"],
.wfDeck__slide[data-pos="1"],
.wfDeck__slide[data-pos="-1"],
.wfDeck__slide[data-pos="2"],
.wfDeck__slide[data-pos="-2"] {
    opacity: 1;
    visibility: visible;
    transition:
        transform 0.75s var(--wf-ease),
        opacity 0.45s ease,
        visibility 0s linear 0s;
}

.wfDeck__slide[data-pos="0"] {
    z-index: 5;
    cursor: default;
}

.wfDeck__slide[data-pos="-1"] { --x: -0.84; --s: 0.84; --r: 26deg; z-index: 4; }
.wfDeck__slide[data-pos="1"]  { --x: 0.84;  --s: 0.84; --r: -26deg; z-index: 4; }
.wfDeck__slide[data-pos="-2"] { --x: -1.5;  --s: 0.7;  --r: 34deg; z-index: 3; opacity: 0.6; }
.wfDeck__slide[data-pos="2"]  { --x: 1.5;   --s: 0.7;  --r: -34deg; z-index: 3; opacity: 0.6; }
.wfDeck__slide[data-pos="-3"] { --x: -2;    --s: 0.6;  --r: 40deg; }
.wfDeck__slide[data-pos="3"]  { --x: 2;     --s: 0.6;  --r: -40deg; }
.wfDeck__slide[data-pos="far"] { --s: 0.5; }

/* A browser tab: chrome bar with the template's path, then the page. */
.wfDeck__sheet {
    position: relative;
    display: flex;
    flex-direction: column;
    width: 100%;
    height: 100%;
    overflow: hidden;
    background: #fff;
    border-radius: 1.2cqw;
    box-shadow:
        0 0 0 1px rgba(5, 0, 56, 0.08),
        0 10px 24px -12px rgba(5, 0, 56, 0.3);
    transition: box-shadow 0.5s ease;
}

.wfDeck__slide[data-pos="0"] .wfDeck__sheet {
    box-shadow:
        0 0 0 1px rgba(5, 0, 56, 0.08),
        0 24px 48px -18px color-mix(in srgb, var(--homeAccent) 60%, transparent);
}

/* Sheets behind the front one fade into the board. */
.wfDeck__sheet::after {
    content: "";
    position: absolute;
    inset: 0;
    background: color-mix(in srgb, var(--homeAccent) 6%, #fff);
    opacity: 0.5;
    pointer-events: none;
    transition: opacity 0.5s ease;
}

.wfDeck__slide[data-pos="0"] .wfDeck__sheet::after {
    opacity: 0;
}

.wfDeck__slide:not([data-pos="0"]):hover .wfDeck__sheet::after {
    opacity: 0.2;
}

.wfDeck__chrome {
    flex: none;
    display: flex;
    align-items: center;
    gap: 0.7cqw;
    height: 3.4cqw;
    padding: 0 1.2cqw;
    background: #f4f3f7;
    border-bottom: 1px solid rgba(5, 0, 56, 0.08);
}

.wfDeck__chrome i {
    flex: none;
    width: 0.9cqw;
    height: 0.9cqw;
    border-radius: 50%;
    background: #d6d4de;
}

.wfDeck__chrome i:first-child {
    background: var(--homeAccent);
}

.wfDeck__chrome span {
    flex: 1 1 auto;
    min-width: 0;
    margin: 0 0 0 0.8cqw;
    padding: 0.25cqw 1cqw;
    overflow: hidden;
    border-radius: 99px;
    background: #fff;
    color: rgba(5, 0, 56, 0.55);
    font-family: var(--font-meta, ui-monospace, monospace);
    font-size: max(9px, 1.3cqw);
    line-height: 1.4;
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* The page is a size container, so its image can scroll by exactly
   "image height minus window height" (100% − 100cqh). */
.wfDeck__page {
    position: relative;
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
    container-type: size;
}

/* The whole page at its own aspect ratio; this is what scrolls. */
.wfDeck__shot {
    position: relative;
    aspect-ratio: var(--ar);
    background-color: #fff;
    background-repeat: no-repeat;
    background-size: 1200% var(--sh);
    background-position: calc(var(--i) / 11 * 100%) 0;
}

.wfDeck.is-near .wfDeck__shot {
    background-image: url("../../work/media/Templates/wireframes/sprite.webp");
}

.wfDeck__shot img {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    max-width: none;
    margin: 0;
}

/* Hidden at once, faded in when loaded (the transition is on the loaded state). */
.wfDeck.is-ready .wfDeck__shot img:not(.is-loaded) {
    opacity: 0;
}

.wfDeck__shot img.is-loaded {
    transition: opacity 0.35s ease;
}

.wfDeck__thumb {
    position: absolute;
    top: 3px;
    right: 3px;
    width: 4px;
    height: 22%;
    border-radius: 4px;
    background: rgba(5, 0, 56, 0.3);
    opacity: 0;
    transition: opacity 0.3s ease;
}

.wfDeck__slide[data-pos="0"] .wfDeck__thumb {
    opacity: 1;
}

@media (prefers-reduced-motion: no-preference) {
    .wfDeck__slide[data-pos="0"] .wfDeck__shot {
        animation: wfScroll var(--wf-dur) ease-in-out 0.6s 1 both;
    }

    .wfDeck__slide[data-pos="0"] .wfDeck__thumb {
        animation: wfThumb var(--wf-dur) ease-in-out 0.6s 1 both;
    }

    .wfDeck.is-paused .wfDeck__slide[data-pos="0"] .wfDeck__shot,
    .wfDeck.is-paused .wfDeck__slide[data-pos="0"] .wfDeck__thumb {
        animation-play-state: paused;
    }
}

@keyframes wfScroll {
    0%, 8%    { transform: translateY(0); }
    92%, 100% { transform: translateY(calc(-100% + 100cqh)); }
}

@keyframes wfThumb {
    0%, 8%    { transform: translateY(0); }
    92%, 100% { transform: translateY(calc(100cqh - 100% - 6px)); }
}

/* Caption and controls under the stage. */
.wfDeck__bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 10px 12px 10px 18px;
    border-top: 1px solid color-mix(in srgb, var(--homeAccent) 16%, transparent);
    background: rgba(255, 255, 255, 0.72);
}

.wfDeck__caption {
    display: flex;
    align-items: baseline;
    gap: 12px;
    min-width: 0;
    margin: 0;
    font-size: 0.875rem;
    line-height: 1.3;
    text-align: left;
}

.wfDeck__count {
    flex: none;
    color: color-mix(in srgb, var(--wf-ink) 45%, transparent);
    font-family: var(--font-meta, ui-monospace, monospace);
    font-size: 0.75rem;
    letter-spacing: 0.04em;
}

.wfDeck__count b {
    font-weight: 400;
    color: color-mix(in srgb, var(--homeAccent) 80%, var(--wf-ink));
}

.wfDeck__name {
    min-width: 0;
    overflow: hidden;
    color: var(--wf-ink);
    font-weight: 600;
    white-space: nowrap;
    text-overflow: ellipsis;
}

.wfDeck__controls {
    flex: none;
    display: flex;
    align-items: center;
    gap: var(--space-2, 8px);
}

/* style.css's `.mainHome :not(a)` repaints every border with !important;
   keep the carousel buttons' dark ring. */
.wfDeck .cs-carousel__prev,
.wfDeck .cs-carousel__next,
.wfDeck .wfDeck__toggle {
    border-color: var(--frgd) !important;
}

/* The controls only work with the script. */
.wfDeck:not(.is-ready) .wfDeck__controls {
    visibility: hidden;
}

/* Prev/next are the site's .cs-carousel__prev/__next (style.css); the pause
   toggle has no equivalent there, so it copies their look. */
.wfDeck__toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    min-height: 36px;
    margin: 0;
    padding: 0;
    border: 2px solid var(--frgd) !important;
    border-radius: 50%;
    background: var(--action);
    color: var(--frgd);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06), inset 0 1px 0 rgba(255, 255, 255, 0.5);
    cursor: pointer;
    transition:
        transform 0.2s cubic-bezier(0.25, 0.1, 0.25, 1),
        box-shadow 0.25s cubic-bezier(0.25, 0.1, 0.25, 1);
}

.wfDeck__toggle:hover {
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.6);
    transform: scale(1.04);
}

.wfDeck__toggle:active {
    transform: scale(0.96);
}

.wfDeck__toggle:focus-visible {
    outline: 2px solid var(--frgd);
    outline-offset: 2px;
}

.wfDeck__toggle svg {
    width: 20px;
    height: 20px;
    fill: currentColor;
}

.wfDeck__icon--play,
.wfDeck.is-stopped .wfDeck__icon--pause {
    display: none;
}

.wfDeck.is-stopped .wfDeck__icon--play {
    display: block;
}

@media (prefers-reduced-motion: reduce) {
    .wfDeck__slide,
    .wfDeck__sheet,
    .wfDeck__sheet::after {
        transition: none;
    }

    /* Nothing moves on its own, so there is nothing to pause. */
    .wfDeck__toggle {
        display: none;
    }
}

/* --- Full-size viewer for a component definition --------------------------
   Opens in place over the board (ds-specs.js), exactly its size: name and
   close on top, the spec in the middle, the case-study link and previous/next
   below. The board stays visible, blurred, around it. */
.dsViewer {
    position: absolute;
    inset: 0;
    z-index: 3;
    display: flex;
    flex-direction: column;
    background: color-mix(in srgb, var(--homeAccent) 8%, rgba(255, 255, 255, 0.82));
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
    color: var(--frgd, #050038);
    line-height: 1.3;
    text-align: left;
    animation: dsViewerIn 0.25s cubic-bezier(0.22, 1, 0.36, 1);
}

.dsViewer[hidden] {
    display: none;
}

/* Focus lands on the viewer itself when it opens; the ring is for its controls. */
.dsViewer:focus {
    outline: none;
}

.dsSpecs.is-viewing .dsSpecs__actions {
    visibility: hidden;
}

@keyframes dsViewerIn {
    from { opacity: 0; transform: scale(0.98); }
    to   { opacity: 1; transform: none; }
}

.dsViewer__head,
.dsViewer__foot {
    flex: none;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px 10px 16px;
}

.dsViewer__foot {
    justify-content: space-between;
}

.dsViewer .dsViewer__count,
.dsViewer .dsViewer__title {
    margin: 0;
    padding: 0;
    text-align: left;
    hyphens: none;
}

.dsViewer .dsViewer__count {
    flex: none;
    color: rgba(5, 0, 56, 0.45);
    font-family: var(--font-meta, ui-monospace, monospace);
    font-size: 0.75rem;
    letter-spacing: 0.04em;
}

.dsViewer__count b {
    font-weight: 400;
    color: var(--homeAccent);
}

.dsViewer .dsViewer__title {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    font-family: "Bricolage Grotesque", "Bricolage Grotesque Fallback", "Bricolage Grotesque Fallback R", sans-serif;
    font-size: 1.05rem;
    font-weight: 700;
    line-height: 1.2;
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* A size container, so the sheet can fit the spec to whatever room is left
   between the bars (100cqw by 100cqh) at its own aspect ratio. */
.dsViewer__figure {
    flex: 1 1 auto;
    min-height: 0;
    display: grid;
    place-items: center;
    padding: 0 16px;
    container-type: size;
}

/* The sheet takes the full image's shape before it arrives, painted with the
   spec's low-res cell from the board sprite; the full image fades in on top. */
.dsViewer__sheet {
    position: relative;
    aspect-ratio: var(--w) / var(--h);
    width: min(100cqw, calc(100cqh * var(--w) / var(--h)), calc(var(--w) * 1px));
    overflow: hidden;
    background-color: #fff;
    background-repeat: no-repeat;
    border-radius: 8px;
    box-shadow:
        0 0 0 1px color-mix(in srgb, var(--homeAccent) 18%, transparent),
        0 12px 28px -14px rgba(40, 10, 90, 0.35);
}

.dsViewer__sheet img {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    max-width: none;
    margin: 0;
}

.dsViewer__sheet img:not(.is-loaded) {
    opacity: 0;
}

.dsViewer__sheet img.is-loaded {
    transition: opacity 0.3s ease;
}

/* Same look as the card's own "Read case study" (style.css, .readMoreTop). */
body.home #main-content .cs-tldr--home .dsViewer a.dsViewer__case {
    color: color-mix(in srgb, var(--homeAccent) 72%, var(--frgd, #050038)) !important;
    text-decoration-color: color-mix(in srgb, var(--homeAccent) 55%, currentColor);
}

.dsViewer__nav {
    display: flex;
    gap: var(--space-2, 8px);
    margin-left: auto;
}

/* Close copies the carousel buttons, like the deck's pause toggle. */
.dsViewer__close {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    margin: 0;
    padding: 0;
    border: 2px solid var(--frgd, #050038);
    border-radius: 50%;
    background: var(--action);
    color: var(--frgd, #050038);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06), inset 0 1px 0 rgba(255, 255, 255, 0.5);
    cursor: pointer;
    transition:
        transform 0.2s cubic-bezier(0.25, 0.1, 0.25, 1),
        box-shadow 0.25s cubic-bezier(0.25, 0.1, 0.25, 1);
}

.dsViewer__close:hover {
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.6);
    transform: scale(1.04);
}

.dsViewer__close:focus-visible {
    outline: 2px solid var(--frgd, #050038);
    outline-offset: 2px;
}

.dsViewer__close svg {
    width: 20px;
    height: 20px;
    fill: currentColor;
}

/* Keep the dark ring against .mainHome's border-color override (see .wfDeck). */
.dsViewer .dsViewer__close,
.dsViewer .cs-carousel__prev,
.dsViewer .cs-carousel__next {
    border-color: var(--frgd, #050038) !important;
}

@media (prefers-reduced-motion: reduce) {
    .dsViewer {
        animation: none;
    }
}
