/* The IRO mark is set in Shippori Mincho, self-hosted with the project.
   It is drawn live on the card, so the face has to be here too. */
@font-face{font-family:'Shippori Mincho';src:url('/assets/fonts/iro/ShipporiMincho-400.woff2') format('woff2');font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:'Shippori Mincho';src:url('/assets/fonts/iro/ShipporiMincho-600.woff2') format('woff2');font-weight:600;font-style:normal;font-display:swap}

/* UI / WEB / BRAND — one page per discipline.
   One image per project. The cards behind it are the dp-pile: they say there is
   more inside without showing it, which is the project page's job. */

.dp {
    position: relative;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 40px;
}

/* The piles share the row rather than each claiming 300px: brand has three and
   ui has five, and five at a fixed width ran off both edges. They cap at 300 so
   a short row does not stretch into something oversized. */
.dp-row {
    display: flex;
    width: 100%;
    align-items: flex-start;
    justify-content: center;
    gap: clamp(20px, 3.6vw, 64px);
    perspective: 1700px;
    perspective-origin: 50% 47%;
}

.dp-pile {
    position: relative;
    display: block;
    color: inherit;
    text-decoration: none;
    flex: 1 1 0;
    min-width: 0;
    max-width: 300px;
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
    text-align: left;
    transform-style: preserve-3d;
}


.dp-face {
    position: relative;
    display: block;
    width: 100%;
    aspect-ratio: 3 / 2;
    background: #eeeeec center / cover no-repeat;
    outline: 1px solid rgba(0, 0, 0, 0.09);
    box-shadow: 0 26px 40px -34px rgba(0, 0, 0, 0.45);
    transform: translate3d(0, 0, 0);
    transition: transform 0.42s cubic-bezier(0.16, 1, 0.3, 1),
                box-shadow 0.42s ease, outline-color 0.22s ease;
}

/* Grounds are fixed, so on a dark page a black plate (Zero) has no edge and
   disappears into the background. The plate does not change; it gets a rim.
   On the white cards a light rim is invisible, which is correct — white
   separates itself. */
/* One ground for every cover, the one Micro Interactions already used. Dark mode
   keeps its own, because a light plate on a black page is a hole. */
.dp-face { --plate: #F4F4F4; --ink: #0A0A0A; }
body.dark .dp-face {
    --plate: #121314;                       /* a hair off the page, or a black
                                               card on a black page has no body */
    --ink: #ECECEC;
    outline-color: rgba(255, 255, 255, 0.16);
}
body.dark .dp-pile:hover .dp-face,
body.dark .dp-pile:focus-visible .dp-face { outline-color: rgba(255, 255, 255, 0.5); }

/* pointing at a dp-pile lifts the top sheet off the ones beneath */
.dp-pile:hover .dp-face,
.dp-pile:focus-visible .dp-face {
    transform: translate3d(0, -10px, 90px);
    outline-color: rgba(0, 0, 0, 0.55);
    box-shadow: 0 46px 64px -38px rgba(0, 0, 0, 0.4);
}

/* Not built yet: an outline holding the name, and it does not open. */
.dp-face.is-blank {
    background: none;
    outline: 1px dashed rgba(0, 0, 0, 0.22);
    box-shadow: none;
}
body.dark .dp-face.is-blank { outline-color: rgba(255, 255, 255, 0.26); }
.dp-pile.is-empty { cursor: default; }
.dp-pile.is-empty:hover .dp-face { transform: none; outline-color: rgba(0, 0, 0, 0.3); box-shadow: none; }
body.dark .dp-pile.is-empty:hover .dp-face { outline-color: rgba(255, 255, 255, 0.4); }

.dp-name {
    display: flex;
    gap: 12px;
    justify-content: center;
    margin-top: 22px;
    font-size: 11px;
    line-height: 18px;
    color: var(--primary);
}
.dp-name .dim { color: var(--muted); }
.dp-pile:hover .dp-name,
.dp-pile:focus-visible .dp-name { font-weight: 500; }

/* the others step back while one is being looked at */
.dp-row.is-reading .dp-pile:not(:hover) .dp-face { opacity: 0.32; }
.dp-row .dp-face { transition-property: transform, box-shadow, outline-color, opacity; }

.dp-empty {
    font-size: 11px;
    line-height: 20px;
    color: var(--muted);
    max-width: 34ch;
    text-align: center;
}

@media (max-width: 860px) {
    .dp { padding: 0 24px; overflow-y: auto; }
    .dp-row { flex-direction: column; align-items: center; gap: 48px; padding: 96px 0 120px; }
    .dp-pile { flex: none; width: min(320px, 78vw); max-width: none; }
}

/* A mark set in type, drawn live on the card so it stays sharp and can move.
   Same furniture as the project cover, scaled to the card. */
.dp-face.dp-mark {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    background: var(--plate);
    line-height: 1;
    color: var(--ink);
    font-family: 'Shippori Mincho', 'Times New Roman', serif;
}
/* Every mark lifts a little inside its card when you point at it — the move
   Internet Court's icon already made, now the house rule. It rides on the
   `translate`/`scale` longhands rather than the `transform` shorthand, so the
   marks that animate their own transform — the turning plus, the eight dots
   flying in — keep doing it underneath instead of being overwritten. */
.dp-face.dp-mark > * {
    transition: translate 0.42s cubic-bezier(0.16, 1, 0.3, 1),
                scale 0.42s cubic-bezier(0.16, 1, 0.3, 1),
                transform 0.5s cubic-bezier(0.16, 1, 0.3, 1),
                letter-spacing 0.5s ease;
}
.dp-pile:hover .dp-face.dp-mark > *,
.dp-pile:focus-visible .dp-face.dp-mark > * { translate: 0 -5px; scale: 1.03; }
@media (prefers-reduced-motion: reduce) {
    .dp-face.dp-mark > * { transition: none; }
    .dp-pile:hover .dp-face.dp-mark > *,
    .dp-pile:focus-visible .dp-face.dp-mark > * { translate: none; scale: none; }
}

.dp-mark--iro { font-size: clamp(30px, 3.6vw, 52px); }
.dp-mark--iro .w { font-weight: 500; letter-spacing: 0.01em; }
.dp-mark--iro .k { font-size: 0.84em; margin-top: 0.14em; }
.dp-mark--iro .r { font-size: 0.22em; letter-spacing: 0.1em; margin-top: 0.4em; }

/* it settles into itself on arrival, and again when you point at it */
.dp-mark > span {
    animation: mark-in 0.7s cubic-bezier(0.16, 1, 0.3, 1) both;
    transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1), letter-spacing 0.5s ease;
}
.dp-mark .w { animation-delay: 0.05s; }
.dp-mark .k { animation-delay: 0.16s; }
.dp-mark .r { animation-delay: 0.30s; }
@keyframes mark-in {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
}
/* The mark draws itself in rather than spreading out: the tracking closes up
   and the kanji holds the middle. Pulling together reads as the mark settling;
   pushing apart read as it coming loose. */
.dp-pile:hover .dp-mark .w { letter-spacing: -0.045em; }
.dp-pile:hover .dp-mark .k { transform: scale(1.05); }
.dp-pile:hover .dp-mark .r { letter-spacing: 0.02em; }

@media (prefers-reduced-motion: reduce) {
    .dp-mark > span { animation: none; transition: none; }
}

/* Zero: the mark writes itself on, then inks in. "Everything starts from zero",
   so it starts from nothing. The outline is traced first and fades out as the
   fill arrives, or you would be left with a double-weight edge. */
.dp-face.dp-mark--zero {
    background: var(--plate);
    padding: 0 12%;
}
.dp-mark--zero svg { width: 90%; height: auto; display: block; overflow: visible; }
.dp-mark--zero path {
    fill: var(--ink);
    stroke: var(--ink);
    stroke-width: 9;                 /* pre-matrix units; the group scales it to a hairline */
    stroke-linejoin: round;
    stroke-dasharray: 1;
    animation: zero-draw 2.2s cubic-bezier(0.55, 0, 0.25, 1) 0.15s both;
}
@keyframes zero-draw {
    0%   { stroke-dashoffset: 1; fill-opacity: 0; stroke-opacity: 1; }
    68%  { stroke-dashoffset: 0; fill-opacity: 0; stroke-opacity: 1; }
    100% { stroke-dashoffset: 0; fill-opacity: 1; stroke-opacity: 0; }
}
/* a second name so pointing at it starts the trace again */
.dp-pile:hover .dp-mark--zero path {
    animation: zero-redraw 1.5s cubic-bezier(0.55, 0, 0.25, 1) both;
}
@keyframes zero-redraw {
    0%   { stroke-dashoffset: 1; fill-opacity: 0; stroke-opacity: 1; }
    68%  { stroke-dashoffset: 0; fill-opacity: 0; stroke-opacity: 1; }
    100% { stroke-dashoffset: 0; fill-opacity: 1; stroke-opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
    .dp-mark--zero path,
    .dp-pile:hover .dp-mark--zero path { animation: none; fill-opacity: 1; stroke-opacity: 0; }
}

/* pasternak.cv: the console's Search pill, lifted whole.
   The console is a great many screens and exactly one control you reach for, so the
   control is the better stand-in for it than a letterform was.

   The sheen is the app's own technique: a band twice the width of the box, swept by
   animating background-position rather than a transform, on a pseudo-element so it
   passes over the label and the key as well. One adaptation — on the light cover the
   band is white over the ink pill as in the console, but in dark mode the pill flips
   to paper and the band flips to ink with it, because a white sweep over a white pill
   is nothing at all. That inversion is the console's own rule, borrowed from its
   .nav-drop, which already uses an ink band on light ground. */
.dp-face.dp-mark--pcv { container-type: inline-size; background: var(--plate); }

.dp-mark--pcv .sbp {
    position: relative; overflow: hidden;
    display: inline-flex; align-items: center; gap: 4.65cqw;
    padding: 3.72cqw 6.98cqw;
    border-radius: 999px;
    border: 1px solid rgba(255, 255, 255, 0.10);
    background: rgba(10, 10, 10, 0.92);
    color: rgba(255, 255, 255, 0.72);
    font-family: var(--nhaas), 'Helvetica Neue', Helvetica, Arial, sans-serif;
    box-shadow: 0 1px 2px rgba(10, 10, 10, 0.10),
                0 2.79cqw 9.3cqw rgba(10, 10, 10, 0.18),
                inset 0 1px 0 rgba(255, 255, 255, 0.12);
}
.dp-mark--pcv .sbp-l { font-size: 6.28cqw; letter-spacing: -0.01em; white-space: nowrap; }
.dp-mark--pcv .sbp-k {
    font: inherit; font-size: 4.88cqw; letter-spacing: 0.01em;
    padding: 0.93cqw 2.79cqw; border-radius: 2.33cqw;
    background: rgba(255, 255, 255, 0.13);
    border: 1px solid rgba(255, 255, 255, 0.10);
    color: rgba(255, 255, 255, 0.72);
}
.dp-mark--pcv .sbp::after {
    content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
    background: linear-gradient(100deg, transparent 25%,
        rgba(255, 255, 255, 0.10) 42%, rgba(255, 255, 255, 0.20) 50%,
        rgba(255, 255, 255, 0.10) 58%, transparent 75%);
    background-size: 200% 100%;
    animation: dp-sheen 3.6s linear infinite;
}
@keyframes dp-sheen { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }

/* pointing at it is the console's own hover: the pill goes fully black and lifts */
.dp-pile:hover .dp-mark--pcv .sbp {
    background: rgba(10, 10, 10, 0.98); color: #fff;
    box-shadow: 0 1px 2px rgba(10, 10, 10, 0.12),
                0 4.65cqw 13.02cqw rgba(10, 10, 10, 0.24),
                inset 0 1px 0 rgba(255, 255, 255, 0.18);
}

/* dark plate: the pill becomes paper, and the sweep becomes ink */
body.dark .dp-mark--pcv .sbp {
    background: rgba(244, 244, 244, 0.94);
    border-color: rgba(10, 10, 10, 0.10);
    color: rgba(10, 10, 10, 0.70);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.30), 0 2.79cqw 9.3cqw rgba(0, 0, 0, 0.38),
                inset 0 1px 0 rgba(255, 255, 255, 0.60);
}
body.dark .dp-mark--pcv .sbp-k {
    background: rgba(10, 10, 10, 0.10);
    border-color: rgba(10, 10, 10, 0.10);
    color: rgba(10, 10, 10, 0.60);
}
body.dark .dp-mark--pcv .sbp::after {
    background: linear-gradient(100deg, transparent 25%,
        rgba(10, 10, 10, 0.06) 42%, rgba(10, 10, 10, 0.13) 50%,
        rgba(10, 10, 10, 0.06) 58%, transparent 75%);
    background-size: 200% 100%;
}
body.dark .dp-pile:hover .dp-mark--pcv .sbp { background: #FFFFFF; color: rgba(10, 10, 10, 0.92); }

@media (prefers-reduced-motion: reduce) {
    .dp-mark--pcv .sbp::after { animation: none; background-position: 50% 0; }
}

/* Peprally: the app's own mark — eight dots, two on each arm of a cross.
   They used to fly in from eight directions on load and again on hover. The load
   run was the problem: a row of five covers all animating at once on arrival is
   noise, and by the time you look at any one of them it has finished. So the mark
   is simply there, and the movement belongs to the hover.

   The hover is a clockwise reveal, which is the mark's own logic: the dots are a
   clock face with four hands, so a sweep hand is the one motion the shape already
   implies. --c is each dot's position going clockwise from twelve, outer before
   inner on each arm, so the sweep radiates as it turns. */
.dp-face.dp-mark--peprally { background: var(--plate); }
.dp-mark--peprally svg { width: 32%; height: auto; display: block; overflow: visible; fill: var(--ink); }
.dp-mark--peprally circle { transform-box: fill-box; transform-origin: center; }
.dp-pile:hover .dp-mark--peprally circle,
.dp-pile:focus-visible .dp-mark--peprally circle {
    animation: dp-sweep 0.46s cubic-bezier(0.3, 1.35, 0.5, 1) both;
    animation-delay: calc(var(--c, 0) * 52ms);
}
@keyframes dp-sweep {
    from { transform: scale(0); opacity: 0; }
    to   { transform: none; opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
    .dp-pile:hover .dp-mark--peprally circle { animation: none; }
}

/* Aether: the plus on its own, white ground. It rotates in its own plane
   rather than turning through space — the mark has four-fold symmetry, so every
   quarter turn lands back on itself and the movement reads as a mechanism
   resetting rather than an object flipping. Drawn lighter than the logotype
   weight: at card size the real 19% bar reads as a block, not a plus. */
.dp-face.dp-mark--aether { background: var(--plate); }
.dp-mark--aether .ae-turn {
    display: block;
    width: 26%;
    will-change: transform;
    animation: ae-turn 2.9s cubic-bezier(0.72, 0, 0.18, 1) infinite;
}
.dp-mark--aether .ae-turn svg { width: 100%; height: auto; display: block; fill: var(--ink); }
/* A quarter turn lands the plus back on itself, so the turn is the event and
   the rest is the mark. Rotating continuously means it reads as an X for as
   much of the loop as it reads as a +, which is not a mark any more. */
@keyframes ae-turn {
    0%,  62%  { transform: rotate(0deg); }
    82%, 100% { transform: rotate(90deg); }
}
@media (prefers-reduced-motion: reduce) {
    .dp-mark--aether .ae-turn { animation: none; }
}

/* Switching section is a page load, so without this it cuts. The piles come up
   in order on arrival and drop away together on the way out — the same motion
   either side of the navigation, so it reads as one move rather than two pages. */
.dp-pile { animation: dp-in 0.55s cubic-bezier(0.16, 1, 0.3, 1) both; }
.dp-pile:nth-child(1) { animation-delay: 0.04s; }
.dp-pile:nth-child(2) { animation-delay: 0.10s; }
.dp-pile:nth-child(3) { animation-delay: 0.16s; }
.dp-pile:nth-child(4) { animation-delay: 0.22s; }
.dp-pile:nth-child(5) { animation-delay: 0.28s; }
@keyframes dp-in { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }

.dp-row.is-leaving .dp-pile {
    animation: none;
    opacity: 0;
    transform: translateY(-12px);
    transition: opacity 0.22s ease, transform 0.22s cubic-bezier(0.4, 0, 1, 1);
}
@media (prefers-reduced-motion: reduce) {
    .dp-pile { animation: none; }
    .dp-row.is-leaving .dp-pile { transition: none; }
}

/* a cover without a page is not clickable */
.dp-pile:not(.page-link) { cursor: default; }
.dp-pile:not(.page-link):hover .dp-face { transform: none; }


/* ── Micro Interactions cover: a folder that opens ──────────────────────
   From Jake's folder-card build, which is itself a 1:1 of a gallery card he pasted.
   The silhouette path, the 500px perspective, the pocket tipping -55deg about its
   bottom edge and the three sheet poses are that build's measured values.

   Two things here are this file's, not that one's. The drawing is expressed in
   container units rather than pixels scaled by script, because a cover is 231px wide
   on a wide screen and 172px on a narrower one and a fixed drawing scaled at runtime
   is how a cover ends up soft. And the paper is white with a hairline instead of the
   source's #f4f4f2 — that colour IS this cover's ground, so the sheets would have
   disappeared into it.

   Why it earns the cover: the section is a folder, and pointing at it is the thing
   that tells you there is more than one piece inside. Blue, because a folder is blue —
   the grey one read as a shape and this one reads as a folder before you have thought
   about it. */
.dp-face.dp-mark--uibits { container-type: inline-size; background: var(--plate); }

.dp-mark--uibits .fd {
    position: relative; display: block;
    width: 44cqw; height: 35.5cqw;
}
.dp-mark--uibits .fd s { display: block; text-decoration: none; }
.dp-mark--uibits .fd-defs { position: absolute; width: 0; height: 0; }

.dp-mark--uibits .fd-back {
    position: absolute; inset: 0;
    clip-path: url(#dpFolderClip);
    background: linear-gradient(176deg, #2C2C30 0%, #1E1E21 38%, #151517 100%);
}
body.dark .dp-mark--uibits .fd-back {
    background: linear-gradient(176deg, #ECECEC 0%, #D8D8D8 38%, #C6C6C6 100%);
}

.dp-mark--uibits .fd-papers { position: absolute; inset: 0; transform-style: preserve-3d; }
.dp-mark--uibits .fd-paper {
    position: absolute; left: 50%; bottom: 1.8cqw;
    width: 18.6cqw; height: 24.2cqw; margin-left: -9.3cqw;
    border-radius: 1cqw; overflow: hidden;
    background: linear-gradient(180deg, #FFFFFF, #F1F1EF);
    box-shadow: 0 0 0 1px rgba(0,0,0,.07), 0 1.8cqw 3.4cqw rgba(0,0,0,.20);
    transform: translate(0, 0) scale(1);
    transition: transform .52s cubic-bezier(.22,.9,.24,1);
}
.dp-pile:hover .dp-mark--uibits .fd-l { transform: translateX(-13cqw) translateY(-16cqw) scale(1.1) rotate(-8deg); }
.dp-pile:hover .dp-mark--uibits .fd-m { transform: translateY(-17cqw) scale(1.1); }
.dp-pile:hover .dp-mark--uibits .fd-r { transform: translateX(13cqw) translateY(-16cqw) scale(1.1) rotate(8deg); }
/* the outer two lead, the middle one follows */
.dp-mark--uibits .fd-l { transition-delay: 0s; }
.dp-mark--uibits .fd-m { transition-delay: .055s; }
.dp-mark--uibits .fd-r { transition-delay: .028s; }

.dp-mark--uibits .fd-sheet { padding: 1.6cqw; display: flex; flex-direction: column; gap: 1.2cqw; height: 100%; }
.dp-mark--uibits .fd-head { display: flex; align-items: center; gap: 0.9cqw; }
.dp-mark--uibits .fd-meta { display: flex; flex-direction: column; gap: 0.5cqw; flex: 1; }
.dp-mark--uibits .fd-lines { display: flex; flex-direction: column; gap: 0.8cqw; }
.dp-mark--uibits .fd-chips { display: flex; gap: 0.6cqw; margin-top: 0.4cqw; }
.dp-mark--uibits .sk { background: #D3D3D0; border-radius: 0.4cqw; }
.dp-mark--uibits .fd-av { width: 4cqw; height: 4cqw; border-radius: 50%; flex: none; }
.dp-mark--uibits .fd-dot { width: 2.6cqw; height: 2.6cqw; border-radius: 50%; flex: none; }
.dp-mark--uibits .fd-ln { height: 0.8cqw; }
.dp-mark--uibits .w35 { width: 35%; } .dp-mark--uibits .w45 { width: 45%; }
.dp-mark--uibits .w55 { width: 55%; } .dp-mark--uibits .w60 { width: 60%; }
.dp-mark--uibits .w70 { width: 70%; } .dp-mark--uibits .w75 { width: 75%; }
.dp-mark--uibits .w85 { width: 85%; } .dp-mark--uibits .w90 { width: 90%; }
.dp-mark--uibits .fd-chip { width: 3.4cqw; height: 1.4cqw; border-radius: 0.7cqw; flex: none; }
.dp-mark--uibits .fd-block { height: 5.4cqw; border-radius: 0.6cqw; flex: none; }
.dp-mark--uibits .fd-block.short { height: 3.4cqw; }

/* The pocket is clipped to the folder's own silhouette. Tipping it 55deg about its
   bottom edge throws the top edge toward you, and a nearer edge projects wider —
   about 1.9x the folder at that height. A pocket cannot be wider than its folder,
   so the silhouette is both the fix and the honest answer. */
.dp-mark--uibits .fd-frontclip { position: absolute; inset: 0; clip-path: url(#dpFolderClip); }
.dp-mark--uibits .fd-frontwrap {
    position: absolute; inset: 0; transform-style: preserve-3d;
    transform-origin: center bottom;
    transform: perspective(50cqw) rotateX(0deg);
    transition: transform .52s cubic-bezier(.22,.9,.24,1);
}
.dp-pile:hover .dp-mark--uibits .fd-frontwrap { transform: perspective(50cqw) rotateX(-55deg); }
.dp-mark--uibits .fd-front {
    position: absolute; left: 0; right: 0; bottom: 0; height: 80%;
    border-radius: 1.6cqw 1.6cqw 2.6cqw 2.6cqw;
    background: linear-gradient(180deg, #3C3C41 0%, #27272B 42%, #17171A 100%);
    box-shadow: inset 0 1px 0 rgba(255,255,255,.16), inset 0 0 0 1px rgba(255,255,255,.06),
                0 -1cqw 2.6cqw rgba(0,0,0,.36);
    overflow: hidden;
}
body.dark .dp-mark--uibits .fd-front {
    background: linear-gradient(180deg, #FAFAFA 0%, #E6E6E6 42%, #D0D0D0 100%);
    box-shadow: inset 0 1px 0 rgba(0,0,0,.10), inset 0 0 0 1px rgba(0,0,0,.05),
                0 -1.529cqw 3.976cqw rgba(0,0,0,.30);
}
/* on a light folder the paper has to darken or the sheets vanish into it */
body.dark .dp-mark--uibits .fd-paper {
    background: linear-gradient(180deg, #37373B, #2A2A2E);
    box-shadow: 0 0 0 1px rgba(255,255,255,.10), 0 2.753cqw 5.2cqw rgba(0,0,0,.45);
}
body.dark .dp-mark--uibits .sk { background: #4E4E54; }
.dp-mark--uibits .fd-noise {
    position: absolute; inset: 0; opacity: .16; mix-blend-mode: overlay;
    background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='dpNoise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23dpNoise)'/%3E%3C/svg%3E");
}

@media (prefers-reduced-motion: reduce) {
    .dp-mark--uibits .fd-paper, .dp-mark--uibits .fd-frontwrap { transition: none; }
}

.dp-pile:hover .dp-face.dp-mark--uibits > *,
.dp-pile:focus-visible .dp-face.dp-mark--uibits > * { translate: none; scale: none; }

/* Mass — the wordmark, as the project page sets it: lowercase, one weight, no mark
   beside it. The page runs it at clamp(3.5rem, 11vw, 8.5rem) with -0.04em; at cover
   size the tracking closes a little further, because four round letters at 78px drift
   apart into a row of shapes if they keep a display setting. */
.dp-face.dp-mark--mass { container-type: inline-size; background: var(--plate); }
.dp-mark--mass .ms {
    font-family: var(--nhaas), 'Helvetica Neue', Helvetica, Arial, sans-serif;
    font-weight: 500;
    font-size: 30cqw;
    letter-spacing: -0.05em;
    line-height: 1;
    color: var(--ink);
}

/* Ese Guey — the real wordmark, from the client's own Figma (pprq1KnLcAnx1VBaKyJGNC,
   node 50:54): Khand SemiBold, uppercase, -12 tracking on a 300 face. The stand-in
   guessed a heavy rough-cut slab and was wrong in both respects — the mark is a tall
   condensed grotesque with clean edges. Khand is Google/ITF, OFL, subset here to the
   six letters of the lockup: 346KB -> 2.8KB.

   Set in ink, not the brand's #170C03. Every cover on the site is ink on the house
   ground, and #170C03 is so nearly black that keeping it would only break the rule
   without showing a colour. */
@font-face {
    font-family: 'Khand';
    src: url('/assets/fonts/eseguey/Khand-600.woff2') format('woff2');
    font-weight: 600; font-display: swap;
}
.dp-face.dp-mark--eseguey { container-type: inline-size; background: var(--plate); }
/* The site's split-flap counter, spelling the name. Their geometry, kept: the tile is
   1.6074 times its width, the corner is .1185 of it, the glyph is Khand 600 at 1.5x the
   tile at -.02em, the gap is .06 and the word space .34.

   INVERTED against the brand. On the real site the tiles are #2B221C with #FC7804 type;
   here they are ink with plate letters, because this row is monochrome and brown-on-orange
   would be the one coloured thing in it. It also means the cover follows the theme for
   free — in dark mode the tiles go pale and the letters dark, still their object. */
.dp-mark--eseguey .eg {
    display: flex; align-items: center;
    --w: 8cqw;
    font-family: 'Khand', var(--nhaas), 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
.dp-mark--eseguey .eg-sp { flex: none; width: calc(var(--w) * .34); }
.dp-mark--eseguey .eg-f {
    position: relative; flex: none; font-style: normal;
    width: var(--w); height: calc(var(--w) * 1.6074);
    margin-right: calc(var(--w) * .06);
    perspective: calc(var(--w) * 5);
    font-weight: 600; font-size: calc(var(--w) * 1.5); line-height: 1;
    letter-spacing: -.02em; color: var(--plate);
    /* a tile is its own world: nothing inside it can invalidate a neighbour */
    contain: layout paint;
}
.dp-mark--eseguey .eg-f:last-child { margin-right: 0; }
.dp-mark--eseguey .eg-f b {
    position: absolute; left: 0; right: 0; height: 50%; overflow: hidden;
    background: var(--ink); --r: calc(var(--w) * .1185); font-weight: inherit;
}
.dp-mark--eseguey .eg-f .t { top: 0; border-radius: var(--r) var(--r) 0 0; }
.dp-mark--eseguey .eg-f .b { bottom: 0; border-radius: 0 0 var(--r) var(--r); }
/* each half is a window on a double-height glyph, so the two read as one letter cut
   across the middle rather than two letters stacked */
.dp-mark--eseguey .eg-f u {
    position: absolute; left: 0; right: 0; height: 200%;
    display: flex; align-items: center; justify-content: center; text-decoration: none;
}
.dp-mark--eseguey .eg-f .t u { top: 0; }
.dp-mark--eseguey .eg-f .b u { bottom: 0; }
/* Promoted up front, not at hover time. These are the only two halves that move, and
   a layer Chromium has to create AND raster on the first frame of a gesture is a
   gesture that starts late — which is what "a bit laggy" on a 60fps-clean animation
   usually is. Fourteen layers at 24x19 is about 100KB and they never repaint. */
.dp-mark--eseguey .eg-f .ft,
.dp-mark--eseguey .eg-f .fb {
    z-index: 2; backface-visibility: hidden; will-change: transform;
}
.dp-mark--eseguey .eg-f .ft { transform-origin: 50% 100%; }
.dp-mark--eseguey .eg-f .fb { transform-origin: 50% 0; transform: rotateX(90deg); }
.dp-mark--eseguey .eg-f s {
    position: absolute; left: 0; right: 0; top: 50%; height: 1px; margin-top: -.5px;
    background: var(--plate); opacity: .22; z-index: 3;
}

/* Ese Guey's own mechanic: the board RIFFLES. It is a split-flap, so the only honest move
   is the one a split-flap makes — each tile turns over, left to right. The letter underneath never changes: what you see is
   the flap, not a new character, which is what a board does when it re-seats a row it
   already has right.

   42ms apart, NOT the site's 90. On the site this is a loader with time to fill; as a
   hover, 90ms put the last tile 630ms behind the first and the whole gesture at 850ms —
   which reads as lag even though no frame is dropped (measured at
   6x CPU throttle: 16.7ms mean, worst 17.6ms, zero frames over 24), and a seven-stage
   mechanism reads longer than a fade of the same length. 46ms brings the whole gesture to
   486ms. The half-flip stays at ~.105s, which is their mechanic. */
.dp-pile:hover .dp-mark--eseguey .eg-f .ft {
    animation: eg-fall .105s ease-in both;
    animation-delay: calc(var(--n, 0) * 42ms);
}
.dp-pile:hover .dp-mark--eseguey .eg-f .fb {
    animation: eg-catch .105s ease-out both;
    animation-delay: calc(var(--n, 0) * 46ms + .105s);
}
/* This mark is exempt from the house SCALE, and keeps the lift.
   Scaling an ancestor while its descendants rotate in 3D makes Chromium raster the
   whole group at the start scale and then re-raster it — the same trap as the ring
   carousel. The translate is free; the 1.03 was the expensive half. */
.dp-pile:hover .dp-face.dp-mark--eseguey > *,
.dp-pile:focus-visible .dp-face.dp-mark--eseguey > * { scale: none; }
@keyframes eg-fall  { from { transform: rotateX(0); }      to { transform: rotateX(-90deg); } }
@keyframes eg-catch { from { transform: rotateX(90deg); }  to { transform: rotateX(0); } }

/* waffle.ai — their wordmark, borrowed while the work is starting. Measured off
   trywaffle.ai rather than eyeballed: Instrument Sans 600, tracking -0.04em, their cream
   #F2EDE3 on their ground rgb(17,28,26). Only the two colours swap roles here, because
   cream on this cover's #F4F4F4 would be invisible, so the ground becomes the ink. The
   font is theirs and Google's (OFL), subset to the six letters of the word: 964 bytes. */
@font-face {
    font-family: 'Instrument Sans';
    src: url('/assets/fonts/waffle/InstrumentSans-600.woff2') format('woff2');
    font-weight: 600; font-display: swap;
}
.dp-face.dp-mark--waffle { container-type: inline-size; background: var(--plate); }
.dp-mark--waffle .wf {
    font-family: 'Instrument Sans', var(--nhaas), 'Helvetica Neue', Helvetica, Arial, sans-serif;
    font-weight: 600;
    font-size: 17cqw;
    letter-spacing: -0.04em;
    line-height: 1;
    color: #111C1A;
}
body.dark .dp-mark--waffle .wf { color: #F2EDE3; }
/* The letters rise one at a time — a sans with a tight track, so it can move fast. */
.dp-mark--waffle .wf i { display: inline-block; font-style: normal; transition: transform .01s; }
.dp-pile:hover .dp-mark--waffle .wf i {
    animation: wm-rise .52s cubic-bezier(.22, 1.3, .36, 1) both;
    animation-delay: calc(var(--n, 0) * 46ms);
}
@keyframes wm-rise {
    from { transform: translateY(26%) scale(.94); opacity: .25; }
    to   { transform: none; opacity: 1; }
}

/* Internet Court — the app icon from Figma (25ynsQstzk0dtZF2SfEkLu, 57:5901),
   inlined rather than linked so the plate can take the page's ink.

   It was the file's blue-violet. The row reads as one set now: every cover is ink on
   the house ground and nothing on it is coloured, which is how the rest of the site
   has always worked — Zero, Aether and Peprally were already doing this and the three
   app icons were the only things breaking it. The courthouse stays paper, so the icon
   is still a tile with a drawing on it rather than a silhouette. */
.dp-face.dp-mark--court {
    background: var(--plate);
    align-items: center;
    justify-content: center;
    overflow: clip;
}
.dp-face.dp-mark--court .ct-icon {
    width: 30%;
    aspect-ratio: 1;
    display: block;
    filter: drop-shadow(0 10px 22px rgba(10, 10, 10, .20));
    transform-origin: 50% 70%;
    transition: filter .5s cubic-bezier(.22,.9,.24,1);
}
/* An app icon is a physical tile, so it turns rather than grows: a slow roll about its
   own base, which throws the shadow across and back. Growing is what the house lift
   already does to every other mark — this is the thing only a tile can do. */
.dp-pile:hover .dp-face.dp-mark--court .ct-icon {
    animation: ct-turn 2.2s cubic-bezier(.4, 0, .2, 1) both;
    filter: drop-shadow(0 16px 30px rgba(10, 10, 10, .28));
}
@keyframes ct-turn {
    0%   { transform: perspective(420px) rotateY(0deg) rotateX(0deg); }
    38%  { transform: perspective(420px) rotateY(16deg) rotateX(-7deg); }
    72%  { transform: perspective(420px) rotateY(-11deg) rotateX(4deg); }
    100% { transform: perspective(420px) rotateY(0deg) rotateX(0deg); }
}
/* The plate's ink is hardcoded in the markup. Two attempts failed first, both worth
   recording: a CSS rule on `stop-color` matched the stops and did not take, and then
   `stop-color="var(--ink)"` as an attribute did not take either — Chromium leaves a
   presentation attribute's var() unresolved on <stop> and quietly keeps the old paint.
   (mamatoyou's eyes use var() in a `stroke` attribute and that DOES work, which is
   what sent me down the wrong road.) Two flat values and an invert for dark mode. */
body.dark .dp-face.dp-mark--court .ct-icon { filter: invert(1) drop-shadow(0 10px 22px rgba(0,0,0,.5)); }

/* mamatoyou — the mark on the cover's own ground. No cream tile behind her: the
   icon's plate was a second surface sitting on the first, and it did not follow the
   theme.

   She blinks. The blink used to live inside the SVG file, which meant it could only
   ever run on its own clock — nothing outside an <img> can reach into it. The mark is
   inline now, so the eyes take two cues: a slow idle blink so she is alive in the row,
   and a deliberate one the moment you press her. The eyes are drawn as open arcs, so a
   blink is a squeeze to almost nothing and back, with a second, smaller beat after it. */
.dp-face.dp-mark--mamatoyou {
    background: var(--plate);
    align-items: center;
    justify-content: center;
    overflow: clip;
}
.dp-face.dp-mark--mamatoyou .mt-mark {
    width: 30%;
    aspect-ratio: 191 / 190;
    display: block;
}
.dp-mark--mamatoyou .mt-eye {
    transform-box: fill-box;
    transform-origin: center;
    animation: mt-blink 5.4s ease-in-out infinite;
}
/* She answers a look as well as a press. These use DIFFERENT keyframe names from the
   idle loop, and that is the whole reason they work: re-declaring `animation` with the
   same name only changes its timing, so the browser carried on from wherever the 5.4s
   loop happened to be and nothing visible happened. A new name is a new animation, and
   a new animation starts at zero.

   The idle blink is back-loaded — it sits at 90% of a long loop — so it cannot be
   reused here anyway: these have to blink at once. */
/* Slowed right down, and the closed eye is HELD. At 0.80s the lid shut in 160ms and
   opened again immediately, which is a real blink and therefore invisible at this size —
   the eye is about 9px tall on the cover. What reads is the pause: close over ~230ms,
   stay shut for ~200, then open. It is slower than a person blinks on purpose. */
.dp-pile:hover .dp-mark--mamatoyou .mt-eye {
    animation: mt-wink 1.30s cubic-bezier(0.4, 0, 0.2, 1) both;
}
.dp-pile:active .dp-mark--mamatoyou .mt-eye {
    animation: mt-wink-fast 0.82s cubic-bezier(0.4, 0, 0.2, 1) both;
}
@keyframes mt-wink {
    0%        { transform: scaleY(1); }
    18%       { transform: scaleY(.06); }
    33%       { transform: scaleY(.06); }
    52%       { transform: scaleY(1); }
    100%      { transform: scaleY(1); }
}
@keyframes mt-wink-fast {
    0%        { transform: scaleY(1); }
    24%       { transform: scaleY(.06); }
    44%       { transform: scaleY(.06); }
    70%, 100% { transform: scaleY(1); }
}
@keyframes mt-blink {
    0%, 90%, 100% { transform: scaleY(1); }
    93%           { transform: scaleY(.08); }
    96%           { transform: scaleY(1); }
    97.5%         { transform: scaleY(.3); }
    99%           { transform: scaleY(1); }
}
@media (prefers-reduced-motion: reduce) {
    .dp-mark--mamatoyou .mt-eye,
    .dp-pile:hover .dp-mark--mamatoyou .mt-eye,
    .dp-pile:active .dp-mark--mamatoyou .mt-eye { animation: none; }
}
}
}
}
