/* Eleonora Redaelli — portfolio
   Tokens and type are the ones settled in the design canvas. See HANDOFF.md §2:
   white paper, invisible grid, lowercase nav, DM Mono 300/400/500 only.
   The keyframes that drive the figure are appended to this file by the build. */

:root {
  --paper: #FFFFFF;
  --ink:   #1A1A18;   /* headings, name, nav, markers */
  --body:  #6E6E6B;   /* running copy */
  --label: #A0A09B;   /* uppercase micro-labels */
  --rule:  #EAEAE4;
  --rule-soft: #EFEFE9;
  --sage:  #8A9E97;   /* empty plate fill — placeholder only */
  --clay:  #96593F;   /* no longer used: hovers darken to --ink instead */
  --hold:  #B08E76;   /* bracketed lines awaiting her words */

  --margin: 56px;
  --page: 1440px;
  /* Height of the sticky menu, so the opening can fill exactly the rest of the
     first screen. If the menu ever wraps and grows past this, the opening simply
     runs a little long — which is the harmless direction to be wrong in. */
  --head-h: 91px;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--paper);
  font-family: 'DM Mono', 'SFMono-Regular', Menlo, Consolas, monospace;
  font-weight: 300;
  color: var(--body);
  -webkit-font-smoothing: antialiased;
  text-wrap: pretty;
  /* clip, not hidden: `hidden` would make body a scroll container and the
     sticky menu would stop sticking. */
  overflow-x: clip;
}

.page { max-width: var(--page); margin: 0 auto; padding-bottom: 100px; }

/* On any screen wide enough for the desktop composition, the composition is
   fixed: 1440 wide, always, and build/fit.js scales the whole page down as one
   block if the window is narrower. Nothing re-flows and no proportion changes —
   it is the same picture, further away. */
@media (min-width: 1101px) {
  .page { width: var(--page); max-width: none; }
}
.pad  { padding-left: var(--margin); padding-right: var(--margin); }

a { color: var(--ink); text-decoration: none; }
a:hover { color: var(--ink); }

/* ---- type ---------------------------------------------------------------- */

.name { font-size: 12px; font-weight: 400; letter-spacing: 0.3em; text-transform: uppercase; color: var(--ink); }
.tick { font-size: 9.5px; font-weight: 400; letter-spacing: 0.25em; color: var(--ink); }
.lbl  { font-size: 8.5px; font-weight: 400; letter-spacing: 0.28em; text-transform: uppercase; color: var(--label); line-height: 1.9; }
.copy { font-size: 11.5px; line-height: 1.95; letter-spacing: 0.04em; color: var(--body); }
.hold { color: var(--hold); }
.h2   { font-size: 21px; font-weight: 300; line-height: 1.62; letter-spacing: 0.055em; color: var(--ink); }
.rule { height: 1px; background: var(--rule); }
.dim  { opacity: .55; }

/* ---- header -------------------------------------------------------------- */

/* The menu is on every page and stays with you down the scroll. */
.head {
  position: sticky; top: 0; z-index: 50;
  background: var(--paper);
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: start;
  gap: 30px;
  padding: 46px var(--margin) 30px;
}
/* The name sits in the middle column, so it is centred on the page and not on
   whatever is left over beside the menu. */
.head .name { grid-column: 2; justify-self: center; text-align: center; }
.head .nav { grid-column: 3; justify-self: end; }
.nav { position: static; }
/* The menu is a pair of brackets in the corner. Clicking opens the space
   between them: the dots slide away as the links come out, and picking one
   closes it again. The brackets themselves never move — they are the fixed
   thing the gesture happens inside of.

   The open and close are one transition on grid-template-columns, 0fr to 1fr,
   which is the only way to animate to and from a width the content decides
   for itself. Slow on purpose: she asked for gentle. */
.nav { display: flex; align-items: baseline; }
.br {
  font-size: 11px; font-weight: 400; letter-spacing: 0.22em; color: var(--ink);
}
.sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
.dots {
  font: inherit; font-size: 11px; letter-spacing: 0.22em; color: var(--ink);
  background: none; border: 0; padding: 0; cursor: pointer;
  display: inline-block; overflow: hidden;
  max-width: 4em; opacity: 1;
  /* Closing: the dots come back last of all, and slowly. */
  transition: max-width 820ms cubic-bezier(.25,.8,.3,1) 340ms,
              opacity   620ms ease 460ms;
}
.nav.is-open .dots {
  max-width: 0; opacity: 0;
  /* Opening: they go at once, to make room. */
  transition: max-width 780ms cubic-bezier(.25,.8,.3,1),
              opacity   380ms ease;
}

.nav-items {
  display: grid; grid-template-columns: 0fr;
  /* Closing: waits for the links to fade before it starts to shut. */
  transition: grid-template-columns 900ms cubic-bezier(.33,.75,.3,1) 180ms;
}
.nav.is-open .nav-items {
  grid-template-columns: 1fr;
  transition: grid-template-columns 780ms cubic-bezier(.25,.8,.3,1);
}
.nav-inner {
  overflow: hidden; min-width: 0;
  display: flex; gap: 34px; white-space: nowrap; padding: 0;
  opacity: 0;
  /* Closing: the links are the first thing to let go. */
  transition: opacity 420ms ease,
              padding 900ms cubic-bezier(.33,.75,.3,1) 180ms;
}
.nav.is-open .nav-inner {
  opacity: 1; padding: 0 16px;
  transition: opacity 520ms ease 120ms,
              padding 780ms cubic-bezier(.25,.8,.3,1);
}
.nav-items a {
  font-size: 11px; font-weight: 400; letter-spacing: 0.22em; color: var(--label);
  transition: color 260ms ease;
}
/* No rule under a link, and no colour: it simply darkens. The page you are on
   is already dark, which is how it says so. */
.nav-items a:hover, .nav-items a:focus-visible { color: var(--ink); }
.nav-items a[aria-current="page"] { color: var(--ink); }

/* Without the script the links are simply there, and the dots are not. */
html:not(.js) .dots { display: none; }
html:not(.js) .nav-items { grid-template-columns: 1fr; }
html:not(.js) .nav-inner { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .dots, .nav-items, .nav-inner { transition: none; }
}


/* ---- plates -------------------------------------------------------------- */

/* No colour behind a photograph. The sage was here so that an empty plate had
   something to be, but on a plate that is full it can only ever be seen by
   mistake — a sliver at one edge where the picture's height lands on a fraction
   of a device pixel and the rounding leaves a gap. On a screen at three times
   scale that is a visible green line down the side of the image, and it is what
   she is seeing as a border. Only empty plates keep it. */
.plate { overflow: hidden; position: relative; background: none; display: block; width: 100%; border: 0; padding: 0; }
.card.empty .plate, .slot { background: var(--sage); }
.plate img { display: block; width: 100%; height: 100%; object-fit: cover; }
.plate img.contain { object-fit: contain; }
.slot {
  border: 1px dashed #D8D8D2; background: var(--paper);
  display: flex; align-items: center; justify-content: center;
}

/* Slow pan standing in for the clips she wants moving. */
@keyframes pan {
  0%   { transform: scale(1.12) translate(1.4%, -1%); }
  50%  { transform: scale(1.2)  translate(-1.6%, 1.2%); }
  100% { transform: scale(1.12) translate(1.4%, -1%); }
}
.motion img { animation: pan 26s ease-in-out infinite; transform-origin: center; }

/* ---- the projects field -------------------------------------------------- */
/* Free composition: irregular sizes, unequal vertical positions. The grid that
   decided where each plate may start is never drawn. Do not tidy this. */

.field { position: relative; margin-top: 40px; }
.card {
  position: absolute; display: block;
  cursor: pointer; background: none; border: 0; padding: 0; margin: 0;
  text-align: left; font-family: inherit; color: inherit;
}
.card .plate img { transition: transform 1100ms cubic-bezier(.2,.7,.2,1), filter 700ms ease; filter: saturate(.95); }
.card:hover .plate img { transform: scale(1.035); filter: saturate(1.06); }
.card .tick { display: block; padding-top: 15px; opacity: .55; transition: opacity 400ms ease; }
.card:hover .tick { opacity: 1; }
.card:focus-visible { outline: 1px solid var(--ink); outline-offset: 10px; }
.card.empty { cursor: default; }
.card.empty .plate { display: flex; align-items: center; justify-content: center; }
.card.empty .plate .lbl { color: rgba(255,255,255,.8); font-size: 8px; }

/* ---- the figure ---------------------------------------------------------- */

/* The first screen is the animation and nothing else. */
.opening {
  min-height: calc(100svh - var(--head-h));
  display: flex; align-items: center; justify-content: center;
}
/* Her name sits over the figure's head, centred on it, and the two are one
   thing: the name is typed while the figure draws itself, and they finish
   together. */
.open-in { display: flex; flex-direction: column; align-items: center; gap: 34px; }
.omino-box { width: 176px; height: 390px; }

/* The name lives in the header. On the home page it arrives one letter at a
   time and then stays exactly where it is — it does not move, fade, or hand
   over to a second copy of itself. */
.typed { white-space: pre; }
/* A typewriter puts a letter down whole. Steps, not a fade. */
.typed > span { opacity: 0; animation: tap 0.01s steps(1, jump-start) both; }
.typed-sp { opacity: 1 !important; animation: none !important; }
@keyframes tap { to { opacity: 1; } }


/* Nothing but the drawing on the first screen: the menu is held back until the
   name and the figure have both finished. */
.page-home .nav { opacity: 0; pointer-events: none; transition: opacity 900ms ease; }
.page-home.is-open .nav { opacity: 1; pointer-events: auto; }


@media (prefers-reduced-motion: reduce) {
  .typed > span { opacity: 1; animation: none; }
  .page-home .nav { opacity: 1; pointer-events: auto; }
}
.omino { display: block; width: 100%; height: 100%; }

/* ---- moodboard ----------------------------------------------------------- */

.loops-box { position: relative; flex: 0 0 640px; max-width: 100%; aspect-ratio: 640 / 448; }
.loops { position: absolute; inset: 0; width: 100%; height: 100%; }
.loops ellipse {
  fill: none; stroke: #2E2D2A; stroke-width: 1.15; stroke-linecap: round;
  stroke-dasharray: 1400; animation: trace 13s cubic-bezier(.55,0,.45,1) infinite;
}
@keyframes trace {
  0%   { stroke-dashoffset: 1400; }
  30%  { stroke-dashoffset: 0; }
  82%  { stroke-dashoffset: 0; }
  100% { stroke-dashoffset: -1400; }
}
.e1 { animation-delay: 0s    !important; }
.e2 { animation-delay: .45s  !important; }
.e3 { animation-delay: .9s   !important; }
.e4 { animation-delay: 1.35s !important; }
.e5 { animation-delay: 1.8s  !important; }
.e6 { animation-delay: 2.25s !important; }

.node {
  position: absolute; cursor: pointer; background: var(--paper); border: 0;
  padding: 6px 9px 5px; margin: 0; font-family: inherit; font-size: 9.5px;
  font-weight: 400; letter-spacing: 0.22em; text-transform: lowercase;
  color: var(--label); transition: color 350ms ease; white-space: nowrap;
}
.node:hover, .node[aria-selected="true"] { color: var(--ink); }

/* The moodboard wall: twenty holders, irregular, columns rather than a grid so
   the rows never line up. Nothing else on the page until her images arrive. */
/* Four columns that each begin at their own height, so the top of the wall is
   a ragged edge rather than a ruled line. */
.wall { display: flex; align-items: flex-start; gap: 170px; margin-top: 110px; }
.wall-col { flex: 1 1 0; min-width: 0; }
.wall .slot { break-inside: avoid; margin-bottom: 46px; width: 100%; }
/* One reference on the wall. Nothing is cropped here — every picture keeps its
   own proportion, which is what makes the column fall unevenly. */
.ff-name { display: block; margin-top: 14px; }
/* How far a picture leans off its column's left edge, and how much room it
   leaves on the right. --sway scales both: a narrow column has no room for a
   third of its width in white space, so the lean flattens as the screen does,
   and the picture widens to take back what the lean gave up. */
.wall { --sway: 1; }
.mb {
  margin: 0 0 150px;
  margin-left: calc(var(--ind, 0) * var(--sway) * 1%);
  width: calc(100% - (var(--ind, 0) + var(--slack, 0)) * var(--sway) * 1%);
}
.mb img { display: block; width: 100%; height: auto; }
.mb figcaption { margin-top: 20px; }
@media (max-width: 1100px) { .wall { gap: 90px; --sway: 0.55; } }
@media (max-width: 767px) {
  /* Two columns on a phone, and the drop between them much smaller — a 232px
     head start is a screenful when the screen is 390 wide. */
  .wall { flex-wrap: wrap; gap: 34px; }
  .wall-col { flex: 1 1 calc(50% - 34px); padding-top: 0 !important; }
  .wall-col:nth-child(2) { padding-top: 54px !important; }
  .wall { --sway: 0.3; }
  .mb { margin-bottom: 70px; }
}

/* ---- the drawing diagrams ------------------------------------------------ */
/* Runs once, not on a loop, with fill-mode both — so it draws itself on load. */

.dg { display: block; width: 100%; height: 100%; }
.dg .dl {
  stroke-dasharray: var(--l); stroke-dashoffset: var(--l);
  animation: dgline var(--d) cubic-bezier(.25,.55,.35,1) var(--t) both;
}
@keyframes dgline { to { stroke-dashoffset: 0; } }
.dg .dw { opacity: 0; animation: dgword .75s ease var(--t) both; }
@keyframes dgword { to { opacity: 1; } }
/* Held at frame zero until it is on screen — assets/js/diagram.js releases it
   by adding .go. Only where JS is running: without it, .js is never set and the
   diagram draws itself on load exactly as it used to. */
.js .dg .dl, .js .dg .dw { animation-play-state: paused; }
.js .dg.go .dl, .js .dg.go .dw { animation-play-state: running; }
@media (prefers-reduced-motion: reduce) {
  .js .dg .dl, .js .dg .dw { animation-play-state: running; }
}
.dg-box { width: 780px; max-width: 100%; flex: 0 0 auto; }
/* With no premise beside it the diagram opens the project on its own: small,
   and centred on the page. */
.dg-alone { width: 100%; max-width: 470px; margin: 70px auto 0; }

/* ---- project page -------------------------------------------------------- */

/* The title is a title page: marker, name, and directly beneath it where and
   when the project is — all centred, before anything else begins. */
.closer { display: flex; justify-content: flex-end; padding-top: 30px; }
.proj-head { text-align: center; margin-top: 80px; }
.proj-title { margin: 26px 0 0; font-size: 38px; font-weight: 300; letter-spacing: 0.2em; text-transform: uppercase; color: var(--ink); line-height: 1.3; }
.proj-where { margin-top: 26px; }
.sect { margin-top: 90px; }
.sect-in { display: flex; gap: 90px; padding-top: 40px; }
.sect-in-plain { gap: 0; padding-top: 0; }
.sect-n { width: 150px; flex: 0 0 150px; }
.sect-body { flex-grow: 1; min-width: 0; }
.cols { display: flex; gap: 90px; margin-top: 40px; flex-wrap: wrap; }
.cols .copy { width: 400px; max-width: 100%; }
/* Flat and expanded: one paragraph across the page rather than a narrow column. */
.cols-wide .copy { width: 100%; max-width: none; }
/* One fixed measure for both columns, so the block reads as a block. */
.cols-rigid { gap: 70px; margin-top: 58px; }
.cols-rigid .copy { width: 430px; flex: 0 0 430px; max-width: 100%; }

/* ---- the rooms ------------------------------------------------------------ */
/* The ten areas of the office beside the plan they belong to. The swatch is the
   colour each wears on the drawing — it is what ties a name to a zone, exactly
   as the portfolio page ties them. */
.rooms-row { display: flex; gap: 80px; margin-top: 46px; align-items: flex-start; flex-wrap: wrap; }
.rooms-fig { flex: 0 1 auto; margin: 0; min-width: 260px; }
.rooms-fig img { display: block; width: 100%; height: auto; }
.rooms-col { flex: 1 1 360px; min-width: 280px; }
.rooms { list-style: none; margin: 0; padding: 0; }
.room {
  display: flex; align-items: baseline; gap: 12px;
  font-size: 10.5px; letter-spacing: 0.1em; line-height: 2.5;
  text-transform: uppercase; color: var(--ink);
}
.room-dot { flex: 0 0 auto; width: 22px; height: 13px; border-radius: 2px; align-self: center; }
/* The name is set in capitals; what it is, in brackets beside it, is not. */
.room-f { color: var(--label); text-transform: lowercase; }
/* The second paragraph sits under the list, in the same column. */
.rooms-note { margin-top: 46px; max-width: 430px; }
@media (max-width: 767px) {
  .rooms-row { gap: 40px; }
  .rooms-fig { flex-basis: auto !important; width: 100%; min-width: 0; }
  .room { font-size: 9.5px; letter-spacing: 0.06em; }
}
.figs { display: flex; gap: 46px; margin-top: 66px; align-items: flex-start; flex-wrap: wrap; }
.cap { margin-top: 16px; font-size: 8.5px; }
/* Her own caption form: lowercase, in brackets, as the portfolio sets them. */
.cap-plain { text-transform: none; letter-spacing: 0.2em; }
.ref { display: flex; gap: 60px; margin-top: 56px; padding-top: 30px; border-top: 1px solid var(--rule-soft); flex-wrap: wrap; }
.ref-n { width: 260px; flex: 0 0 260px; }
.ref-name { margin-top: 18px; font-size: 15px; font-weight: 300; letter-spacing: 0.13em; text-transform: uppercase; color: var(--ink); line-height: 1.7; }
.ref-c { width: 330px; flex: 0 0 330px; max-width: 100%; }
.ref-c .lbl { font-size: 8.5px; letter-spacing: 0.24em; }
.ref-c .copy { margin-top: 14px; }
/* ---- the material board -------------------------------------------------- */
/* Archaeological: the samples cut out and set down on white, sitting on a
   common line with air around them, and the names running down the side in
   lowercase. The numbers are the only thing tying object to name. */

.matboard { display: block; margin-top: 50px; }
/* A timeline: one horizontal band, scrolled sideways, running off both edges of
   the page. The samples hang at uneven heights along it. */
.spec-field {
  display: flex; flex-wrap: nowrap; align-items: flex-start;
  gap: 78px;
  overflow-x: auto; overflow-y: hidden;
  margin-left: calc(-1 * var(--margin)); margin-right: calc(-1 * var(--margin));
  padding: 0 var(--margin) 26px;
  scrollbar-width: none;
}
.spec-field::-webkit-scrollbar { display: none; }
/* The samples stand on a common lower line, whatever their height, with the
   number and the name below each of them. The slot is fixed-depth: the picture
   is pinned above the caption, the caption to the floor. */
.spec { flex: 0 0 auto; position: relative; height: 300px; margin: 0; width: var(--w); }
.spec img { position: absolute; bottom: 62px; left: 0; width: 100%; height: auto; }
.spec figcaption {
  position: absolute; left: 0; top: 238px; width: 120px; height: auto; margin: 0;
}
.spec-n { display: block; font-size: 8.5px; letter-spacing: 0.24em; color: var(--label); }
.spec-name {
  display: block; margin-top: 7px;
  font-size: 10px; line-height: 1.5; letter-spacing: 0.05em;
  color: var(--body); text-transform: lowercase;
}

/* ---- the drawing field --------------------------------------------------- */
/* Everything the portfolio carries as drawing — axonometrics, zoning, plans,
   sections — placed on the invisible 12-column grid at irregular sizes and
   unequal drops. Scrolled through, not tiled. */

.dfield {
  display: grid; grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: 24px; row-gap: 90px; align-items: start; margin-top: 130px;
}
.dfig { margin: 0; }
.dfig img { display: block; width: 100%; height: auto; }
/* The plan, and the same plan rendered, cross-fading under the pointer. The
   overlay is `contain`, so the two need not be framed identically to register. */
.dfig-swap { position: relative; }
.dfig-swap .dfig-hover {
  position: absolute; left: 0; top: 0; width: 100%; height: 100%;
  object-fit: contain; background: var(--paper);
  opacity: 0; transition: opacity 420ms ease;
}
.dfig-swap:hover .dfig-hover, .dfig-swap:focus-within .dfig-hover { opacity: 1; }
/* Nothing to hover with, so the rendered plan would never be seen — set it
   under the line drawing instead of hiding it. */
/* No pointer: the swap happens when the drawing reaches the middle of the
   screen instead. assets/js/swap.js sets the class; see the comment there. */
.dfig-swap.is-on .dfig-hover { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .dfig-swap .dfig-hover { transition: none; }
}

/* ---- the mark ------------------------------------------------------------ */
/* Her figure signs off at the foot of every page — the same drawing as the
   opening, small, and already finished rather than drawing itself again. */

.mark { display: flex; justify-content: center; padding: 110px 0 10px; }
.mark-box { width: 44px; height: 98px; opacity: .75; }
.omino-mark .km { stroke-dashoffset: 0 !important; animation: none !important; }

/* ---- about --------------------------------------------------------------- */
/* Her portfolio's own arrangement: three columns of facts on the left, her
   text down the right beside the portrait. */

.about { display: flex; gap: 90px; margin-top: 70px; align-items: flex-start; flex-wrap: wrap; }
.ab-facts { flex: 1 1 560px; display: flex; gap: 46px; flex-wrap: wrap; }
.ab-col { flex: 1 1 165px; min-width: 150px; }
.ab-item { margin-top: 18px; font-size: 10.5px; line-height: 2; }
.ab-side { flex: 0 1 330px; min-width: 260px; }
.ab-portrait { width: 100%; aspect-ratio: 666 / 420; }
.ab-text { margin-top: 26px; }
.ab-side .ab-text:first-of-type { margin-top: 34px; }
.sub { width: 400px; max-width: 100%; }

/* A section with the number and the heading struck out sits closer to the top. */
.sect-bare { margin-top: 90px; }
.sect-bare .sect-body > *:first-child { margin-top: 0; }
/* Sits close under what comes before it instead of a full band away. */
.sect-tight { margin-top: 30px; }
.sect-tight .aside-row { margin-top: 0; }

/* ---- numbered plates ----------------------------------------------------- */
/* The archive manner: each figure numbered, its caption set in the margin
   beside it rather than beneath, sides alternating so the sequence reads as a
   walk down the page. Plenty of air between plates — the pacing does the
   storytelling, not the density. */

/* The figure field: text left, the three plates scattered right, exactly where
   she put them. Positions are percentages, so the composition scales whole. */
.figfield { position: relative; flex: 1 1 520px; min-width: 300px; }
.ff-item { position: absolute; margin: 0; }
.ff-item .plate { background: var(--paper); }
.ff-cap { margin-top: 14px; }
.ff-cap .tick { display: block; }
.ff-cap .copy { display: block; margin-top: 10px; font-size: 10px; line-height: 1.95; letter-spacing: 0.03em; }

/* fig.01 rides up beside the running text; the text keeps to the left. */
.withfig { display: flex; gap: 70px; margin-top: 40px; align-items: flex-start; flex-wrap: wrap; }
/* The drop sets the text lower than the picture it stands beside — which is
   only a drop while the two are side by side. Stacked on a phone the picture is
   already above, and the same measurement becomes a hole. */
.withfig-text { flex: 0 1 430px; min-width: 280px; display: flex; flex-direction: column; gap: 26px; padding-top: var(--drop, 0px); }
.pl.pl-beside { flex: 0 0 auto; margin: 0; display: block; align-items: initial; gap: 0; }
.pl.pl-beside .pl-cap { flex: none; padding-bottom: 0; margin-top: 16px; max-width: 260px; text-align: left; }

/* A drawing at the left of a section with her sub-texts beside it. */
.aside-row { display: flex; gap: 70px; margin-top: 46px; align-items: flex-start; flex-wrap: wrap; }
.aside-fig { flex: 0 1 auto; margin: 0; min-width: 260px; }
.aside-imgs { display: flex; gap: 30px; align-items: flex-start; flex-wrap: wrap; }
/* One picture in the row, with room for a caption of its own underneath. */
.aside-one { display: block; min-width: 0; }
.aside-one .cap { display: block; margin-top: 14px; }
.aside-fig img { display: block; width: 100%; height: auto; min-width: 0; }
.aside-text { flex: 1 1 600px; min-width: 280px; display: flex; gap: 50px; flex-wrap: wrap; }
.aside-text .sub { flex: 1 1 240px; width: auto; max-width: 520px; min-width: 0; }
.aside-text[style] .sub { max-width: none; }

.plates { display: flex; flex-direction: column; gap: 90px; margin-top: 60px; }
.pl { margin: 0; display: flex; align-items: flex-end; gap: 54px; }
.pl-right { flex-direction: row-reverse; }
.pl-img { flex: 1 1 auto; min-width: 0; }
.pl-img img { display: block; width: 100%; height: auto; }
.pl-cap { flex: 0 0 232px; padding-bottom: 6px; }
.pl-cap .tick { display: block; }
.pl-cap .copy { display: block; margin-top: 12px; font-size: 10px; line-height: 1.95; letter-spacing: 0.03em; }
.pl-right .pl-cap { text-align: right; }

/* ---- a portfolio spread -------------------------------------------------- */
/* The photograph on one side; her sketch and her words on the other. */

.spread { display: flex; gap: 70px; margin-top: 80px; align-items: flex-start; flex-wrap: wrap; }
.spread-img { flex: 1 1 540px; margin: 0; min-width: 0; }

.spread-side { flex: 1 1 440px; min-width: 280px; }
.spread-aside { display: block; width: 100%; max-width: 420px; height: auto; margin: 0 0 40px auto; }

/* ---- the render gallery -------------------------------------------------- */
/* WENTZ [GALERIA]: small pictures at very different sizes, scattered across the
   full width with a lot of air. No labels — the pictures stand on their own.
   Nothing lines up with anything. */

/* Not inside a .sect-body, so it needs no breakout — it already spans the page. */
.gal { position: relative; margin-top: 40px; }
.gal-item { position: absolute; margin: 0; }
/* A field marked data-float has its items moved by assets/js/float.js. The
   will-change is what keeps the drift off the main thread. */
.gal[data-float] > * { will-change: transform; }

/* The lamp, off and lit. Hovering the plate turns it on. Same idea as the
   floorplans, but inside the render field, so the swap lives on the plate. */
.plate-swap { position: relative; }
.plate-swap .plate-hover {
  position: absolute; left: 0; top: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0; transition: opacity 500ms ease;
}
.plate-swap:hover .plate-hover, .plate-swap:focus-within .plate-hover { opacity: 1; }
/* No pointer to turn it on with, so it alternates on its own instead. */
/* Same on a phone: the lamp lights when it is the picture in the middle of the
   screen. One at a time, so the page reads as a sequence. */
.plate-swap.is-on .plate-hover { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .plate-swap .plate-hover { transition: none; animation: none; }
}

/* Not a project: no marker under it, and no pointer change, because there is
   nothing to click. */
.card-extra { cursor: default; }

/* ---- the drawings that fly ------------------------------------------------ */
/* Ceci n'est pas una oficina, and only it: a sun, a bee, a flower loose over the
   whole project. Behind everything, uninterested in the pointer, and moved by
   assets/js/float.js. The page is the positioning context, so a percentage top
   is a percentage of the project however long it turns out to be. */
.page-floats { position: relative; }
.floats { position: absolute; inset: 0; pointer-events: none; z-index: 0; overflow: hidden; }
.float-item {
  position: absolute; display: block; opacity: .92;
  will-change: transform;
}
.float-item img { display: block; width: 100%; height: auto; }

/* The bees fly. Five paths, none of them a circle and none the same length, so
   they never fall into step; the drift on scroll is on the box around each, or
   the two transforms would overwrite one another. */
.float-fly { transform-origin: 50% 50%; will-change: transform; }
.float-fly-1 { animation: bee1 27s ease-in-out infinite; }
.float-fly-2 { animation: bee2 34s ease-in-out infinite; }
.float-fly-3 { animation: bee3 21s ease-in-out infinite; }
.float-fly-4 { animation: bee4 41s ease-in-out infinite; }
.float-fly-5 { animation: bee5 18s ease-in-out infinite; }

@keyframes bee1 {
  0%   { transform: translate(0, 0) rotate(0deg); }
  22%  { transform: translate(74px, -30px) rotate(9deg); }
  48%  { transform: translate(26px, -68px) rotate(-7deg); }
  71%  { transform: translate(92px, 18px) rotate(12deg); }
  100% { transform: translate(0, 0) rotate(0deg); }
}
@keyframes bee2 {
  0%   { transform: translate(0, 0) rotate(0deg); }
  30%  { transform: translate(-88px, 26px) rotate(-11deg); }
  55%  { transform: translate(-34px, 74px) rotate(6deg); }
  80%  { transform: translate(-110px, -14px) rotate(-5deg); }
  100% { transform: translate(0, 0) rotate(0deg); }
}
@keyframes bee3 {
  0%   { transform: translate(0, 0) rotate(0deg); }
  26%  { transform: translate(46px, 52px) rotate(14deg); }
  60%  { transform: translate(118px, -8px) rotate(-9deg); }
  100% { transform: translate(0, 0) rotate(0deg); }
}
@keyframes bee4 {
  0%   { transform: translate(0, 0) rotate(0deg); }
  18%  { transform: translate(-52px, -44px) rotate(8deg); }
  44%  { transform: translate(-136px, 12px) rotate(-13deg); }
  76%  { transform: translate(-70px, 62px) rotate(4deg); }
  100% { transform: translate(0, 0) rotate(0deg); }
}
@keyframes bee5 {
  0%   { transform: translate(0, 0) rotate(0deg); }
  35%  { transform: translate(62px, -36px) rotate(-10deg); }
  68%  { transform: translate(14px, 44px) rotate(7deg); }
  100% { transform: translate(0, 0) rotate(0deg); }
}
@media (prefers-reduced-motion: reduce) {
  .float-fly { animation: none; }
}
/* Everything that carries words or pictures sits above them. */
.page-floats > *:not(.floats) { position: relative; z-index: 1; }
@media (max-width: 767px) {
  /* No room to fly in, and they would only sit on top of the reading. */
  .floats { display: none; }
}
.gal-cap { margin-top: 14px; }
/* Words in the render field, beside the pictures they are about. */
.gal-text { margin: 0; }
/* The diagram is one of the pieces in the field, not a plate — no frame, no
   number, and it still draws itself on load. */
.gal-diagram .dg { width: 100%; height: 100%; }
.gal .slot { width: 100%; }

/* ---- the spaces ---------------------------------------------------------- */
/* The index's free field, at room scale: irregular sizes, unequal drops, air
   between. Percentages of the section body so it scales as one composition. */

/* The text stays in its column; the images break out and run the full width of
   the page. Taken from martabonilla.com — pictures floating free at very
   different scales with a lot of air, rather than tidied under the paragraph.
   240px is the section marker column (150) plus its gutter (90). */
.sfield, .dfield, .plates { margin-left: 0; width: 100%; }

.sfield { position: relative; margin-top: 70px; }
.sitem { position: absolute; margin: 0; }
.sitem .plate { background: var(--sage); }
.plate.paper { background: var(--paper); }
.sitem img { transition: transform 1100ms cubic-bezier(.2,.7,.2,1); }

/* ---- references ---------------------------------------------------------- */

.refs { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 46px; margin-top: 54px; }

.cred { display: flex; gap: 26px; padding: 15px 0; border-top: 1px solid var(--rule-soft); }
.cred .k { width: 150px; flex: 0 0 150px; }
.next { display: flex; justify-content: space-between; align-items: baseline; gap: 30px; padding-top: 40px; flex-wrap: wrap; }
.next-t { font-size: 25px; font-weight: 300; letter-spacing: 0.2em; text-transform: uppercase; color: var(--ink); }

/* Marks a project whose sections have not arrived yet. Not an invention —
   a hole, in clay, exactly like every other hole on the site. */
.awaiting { margin-top: 150px; padding-top: 40px; border-top: 1px solid var(--rule); }

/* ---- below the width the canvas was drawn at ----------------------------- */
/* The composition was designed at 1440. Above that it centres; below it scales.
   Under 1100 the field stops being a field and becomes a slow vertical walk —
   still irregular, still airy, never a grid. */

@media (max-width: 1500px) {
  .page { max-width: none; }
}

@media (max-width: 1100px) {
  :root { --margin: 40px; }

  /* The constellation survives the phone. It cannot survive it at these
     proportions — a plate would arrive a hundred pixels wide — so the narrow
     screen keeps the arrangement and changes the scale: the plates grow a lot,
     the field grows taller to make room for them, and each plate holds its place
     ACROSS the page as a share of the room it has left over rather than as a
     fixed percentage. That last part is what stops a widened plate running off
     the right-hand edge: at lf = 1 it finishes flush, never past.

     She asked for this in as many words: rather the pictures big and a little
     less of the page at once, than the whole composition shrunk to nothing.

     Two numbers, as on the desktop. --zoom is how much bigger every plate is;
     --tall is how many times its own width the field stands. Raising one
     without the other is what makes plates touch. */
  /* --zoom and --tall are written on the section by the build: it is the build
     that added the plate heights up, so it is the build that knows how tall the
     stack came out. */
  .field {
    display: block;
    aspect-ratio: 1 / var(--tall) !important;
    height: auto !important;
    width: calc(100% - var(--margin) * 2);
    margin: 90px auto 0;
  }
  /* left, top and width are written on the element itself, so only !important
     can reach past them. */
  .card, .card:nth-child(3n) {
    position: absolute !important;
    width: calc(var(--cwm) * 100%) !important;
    left: calc(var(--lf) * (100% - var(--cwm) * 100%)) !important;
    top: calc(var(--tm) * 100%) !important;
  }

  .opening { height: 560px; }
  .omino-box { width: 140px; height: 310px; }


  .sect-in { flex-direction: column; gap: 24px; }
  .sect-n { width: auto; flex: none; }
  .cols { gap: 40px; }
  .ref { gap: 34px; }
  .ref-n, .ref-c { width: 100%; flex: none; }
  .figs { gap: 34px; }
  .figs > * { width: 100% !important; padding-top: 0 !important; }

  /* No breaking out once everything is a single column. */
  .sfield, .dfield, .plates { margin-left: 0; width: 100%; }

  /* The drawing field and the spaces stop being fields and become columns. */
  .dfield { display: flex; flex-direction: column; gap: 70px; }
  .dfig { margin-top: 0 !important; }
  .sfield { aspect-ratio: auto !important; display: flex; flex-direction: column; gap: 60px; }
  .sitem { position: static !important; width: 100% !important; }

  /* And so does the render field. Without this it keeps its aspect-ratio, which
     on a 390px screen makes the whole composition about 400px tall with every
     picture stacked on top of every other — the pictures are positioned
     absolutely, in percentages of a box that has collapsed. The column follows
     source order, which is why the slots in content.mjs are written in reading
     order rather than in whatever order they were added. */
  .gal { aspect-ratio: auto !important; display: flex; flex-direction: column; gap: 64px; margin-left: 0; width: 100%; }
  .gal-item { position: static !important; width: 100% !important; }
  .gal-diagram > div { width: 100%; }
  /* Nothing drifts on a phone, so nothing needs a layer of its own. */
  .gal[data-float] > * { will-change: auto; transform: none !important; }
  /* Plates stack, caption under the figure, always the same way round. */
  .plates { gap: 80px; }
  /* The beside-plate is sized by its content, so the blanket max-width reset
     below would let it grow to the picture's natural width and push the whole
     page sideways. Here it stacks like everything else. */
  .pl.pl-beside { width: 100%; }
  .pl.pl-beside .pl-img { max-width: 100% !important; }
  .pl.pl-beside .pl-cap { max-width: none; }
  .withfig, .aside-row { gap: 34px; }
  /* Closer, but not touching: enough is kept that the text still reads as a
     second thought rather than as a caption. */
  .withfig-text { padding-top: 40px; }
  .figfield { aspect-ratio: auto !important; display: flex; flex-direction: column; gap: 50px; }
  .ff-item { position: static !important; width: 100% !important; }
  .aside-fig { flex-basis: auto !important; width: 100%; min-width: 0; }
  .aside-text { flex-basis: auto !important; }
  .pl, .pl-right { flex-direction: column; align-items: stretch; gap: 18px; }
  .pl-img { max-width: none !important; }
  .pl-cap, .pl-right .pl-cap { flex: none; text-align: left; padding-bottom: 0; }
  .matboard { gap: 46px; }
  .spec-field { gap: 40px; }
  .proj-title { font-size: 34px; }
  .sect { margin-top: 130px; }
}

@media (max-width: 720px) {
  :root { --margin: 24px; --head-h: 79px; }
  /* Name over menu, deliberately, rather than letting the row wrap into
     something 158px tall — it is sticky, so every pixel is charged twice. */
  .head { flex-direction: row; align-items: flex-start; gap: 13px; padding: 22px var(--margin) 15px; }

  /* On a phone the brackets open downwards, not sideways.
     Five words in a row want 484px; beside the name on a 390px screen there are
     48. So they were all cut off, and "projects" showed as "proj" — which is
     what she photographed. The gesture is the same, the direction is not: the
     links fall under the brackets as a short column, right-aligned beneath them.
     Opened with max-height rather than the grid trick the wide screen uses,
     because here it is a height that has to change and not a width. */
  .nav { position: relative; }
  .nav-items {
    position: absolute; top: calc(100% + 15px); right: 0; z-index: 2;
    display: block; grid-template-columns: none;
    background: var(--paper);
    max-height: 0; overflow: hidden;
    transition: max-height 660ms cubic-bezier(.25,.8,.3,1);
  }
  .nav.is-open .nav-items { max-height: 280px; transition: max-height 600ms cubic-bezier(.25,.8,.3,1); }
  .nav-inner, .nav.is-open .nav-inner {
    display: flex; flex-direction: column; align-items: flex-end;
    gap: 19px; padding: 3px 0 7px; overflow: visible; white-space: nowrap;
  }
  /* And the dots stay. On a wide screen they slide away to make room for the
     links, because the links arrive beside them; here the links arrive below,
     so there is nothing to make room for — and a bracket with nothing between
     it is a menu with no way of being shut again. */
  .nav.is-open .dots { max-width: 4em; opacity: 1; }

  /* Without the script the column is simply already down. */
  html:not(.js) .nav-items { max-height: 280px; }

  .h2 { font-size: 21px; }
  .proj-title { font-size: 27px; letter-spacing: 0.15em; }
  .proj-head { margin-top: 90px; }
  .cred { flex-direction: column; gap: 6px; }
  .cred .k { width: auto; flex: none; }
  .refs { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px; }
  .spec-field { gap: 34px; }
}

@media (prefers-reduced-motion: reduce) {
  .motion img { animation: none; }
  .loops ellipse { animation: none; stroke-dashoffset: 0; }
  .dg .dl { animation: none; stroke-dashoffset: 0; }
  .dg .dw { animation: none; opacity: 1; }
  .omino [class^="k"] { animation: none !important; stroke-dashoffset: 0 !important; }
}

/* ---- the play table -------------------------------------------------------- */
/* Her seven ceramics loose on a sheet of paper. The table is drawn as a faint
   rectangle, because you have to be able to see where the edge is if pieces
   are going to knock against it — but only just: it is paper, not a board. */
/* The table is the one thing on the site that is not a page. Everything else
   is held inside the margins, because everything else is something to read; this
   is somewhere to work, so it goes the full width of the window and out past the
   margins the rest of the site keeps to.

   50% - 50vw is the trick: the element is pulled left by exactly half of what it
   has gained, which centres it on the window rather than on the column. */
.table-wrap {
  margin-top: 12px;
  width: 100vw;
  margin-left: calc(50% - 50vw);
}
/* No frame: the pieces fall down the page itself, not into a box drawn on it.
   The table is still an element — something has to hold them and know where the
   floor is — but it is invisible, and it fills what is left of the screen so
   the fall is the height of the page. Overflow hidden is what lets a piece wait
   above the top edge without hanging over the menu. */
.table {
  position: relative;
  width: 100%;
  height: calc(100svh - 215px);
  min-height: 460px;
  max-height: 1000px;
  background: var(--paper);
  overflow: hidden;
  /* The browser must not take a drag on a piece and scroll the page with it. */
  touch-action: none;
  user-select: none; -webkit-user-select: none;
}
/* Hidden until it has been placed, and place() is what reveals it.
   A piece goes into the table the moment the script runs, with no size and no
   position — and the instant its photograph arrives it takes that photograph's
   full size and sits at the top-left corner, because nothing has told it where
   to be yet. With eight photographs arriving at eight different moments, the
   early ones stand piled in that corner until the last one lands. She saw it:
   a second of every piece stacked in the corner before the fall. */
.piece {
  position: absolute; top: 0; left: 0;
  visibility: hidden;
  transform-origin: 50% 50%;
  cursor: grab;
  will-change: transform;
}
.piece.placed { visibility: visible; }
.piece img {
  display: block; width: 100%; height: 100%;
  /* If a stated proportion ever drifts from the real file, letterbox it rather
     than squash the pot. */
  object-fit: contain;
  pointer-events: none;
}
/* Picked up: it lifts very slightly off the paper, and nothing more. */
.piece.lifted { cursor: grabbing; filter: drop-shadow(0 10px 16px rgba(26, 26, 24, 0.13)); }
.piece.easing { transition: transform 640ms cubic-bezier(.25,.8,.3,1); }

.play-bar { display: flex; justify-content: center; gap: 34px; margin-top: 26px; }
.play-act {
  font: inherit; font-size: 10.5px; font-weight: 400; letter-spacing: 0.2em;
  text-transform: lowercase; color: var(--label);
  background: none; border: 0; padding: 0; cursor: pointer;
  transition: color 240ms ease;
}
.play-act:hover, .play-act:focus-visible { color: var(--ink); }

.play-note { max-width: 560px; margin: 20px auto 0; text-align: center; }

/* The address and the number: grey like every other label, ink when touched. */
/* Three things in a row at the margin, under [contacts] and starting where it
   starts, with nothing ruled above them. */
.con-row { display: flex; justify-content: flex-start; gap: 54px; flex-wrap: wrap; margin-top: 54px; }
.con { color: var(--body); transition: color 240ms ease; }
.con:hover, .con:focus-visible { color: var(--ink); }

@media (max-width: 767px) {
  /* A phone is held upright, so the table is too. */
  .table { height: calc(100svh - 200px); min-height: 380px; }
  .play-bar { gap: 22px; flex-wrap: wrap; justify-content: center; }
}
@media (prefers-reduced-motion: reduce) {
  .piece.easing { transition: none; }
}

/* ---- grain ----------------------------------------------------------------- */
/*
   One layer, made by the browser out of nothing: SVG turbulence written straight
   into the stylesheet. No file to load and nothing to go blurry when someone
   zooms in.

   It lies over the page rather than under it, so it falls on the photographs too
   and not only on the white between them — the way a photograph taken on film is
   grainy right across, rather than in patches.

   There was a paper fibre under it as well. She had it removed: it was a slow,
   wide grain, and slow grain shows you where one tile of it stops and the next
   begins. Fine grain has no such tell, which is why this one stays.

   0.9 x 240 = 216, a whole number. That is what stitchTiles needs in order to
   join a tile to the next invisibly.
*/
body::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;   /* the play table is under here */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.55'/%3E%3C/svg%3E");
  background-size: 240px 240px;
  /* Plain opacity, not a blend mode. A blend mode on an element this size
     forces the whole page into a single composited layer, and on a phone that
     is what makes a sticky header vanish and come back while you scroll — the
     browser stops repainting it until the scrolling settles. The grain is far
     too faint for the difference in how it mixes to be visible; the difference
     in whether the menu stays put is not. */
  opacity: 0.32;
}

/* It does not belong on a printed page, where the paper is already paper. */
@media print { body::after { display: none; } }



/* ---- the sea --------------------------------------------------------------- */
/*
   Her stars and her six fish, over the wall of references. A first look only:
   nothing here is clickable yet.

   The stars are still because she said they should be. The fish cross the band
   from one side to the other and start again, each at its own depth, its own
   size and its own pace, so they never fall into step. The pauses are built in:
   a fish that has just left is off-screen for a while before it comes back, and
   that emptiness is what makes it a sea rather than a carousel.

   Nothing of this exists on a phone. It is 200-odd kilobytes of picture and a
   long slow animation for something a small screen has no room to show.
*/
.sea {
  position: relative;
  /* Taller: she wanted it developed downwards. A floor of 760 rather than a
     plain vh, because every fish's room underneath is arithmetic against this
     number and arithmetic against a moving number cannot be checked. Above the
     floor it grows with the window and the fish, placed by per cent, only get
     more room — never less. */
  height: max(760px, calc(100svh - 190px));
  margin-top: 40px;
  overflow: hidden;
}
.star {
  position: absolute;
  height: auto;
  /* Set back, so the fish read as being in front of them rather than among. */
  opacity: 0.5;
  transform: translate(-50%, -50%);
  pointer-events: none;
}

.fish {
  position: absolute;
  top: var(--y);
  width: var(--w);
  left: 0;
  animation: swim var(--dur) linear var(--delay) infinite;
  will-change: left;
}
/* The rise and fall. It runs on its own clock, at its own depth, per fish —
   which is the whole of what makes it read as swimming rather than as sliding. */
.fish-in {
  position: relative;
  animation: bob var(--bdur) ease-in-out infinite alternate;
  will-change: transform;
}
@keyframes bob {
  from { transform: translateY(calc(-1 * var(--bob))) rotate(calc(-1 * var(--sway))); }
  to   { transform: translateY(var(--bob)) rotate(var(--sway)); }
}

.fish-img { display: block; width: 100%; height: auto; }
/* Drawn looking the other way, so it is turned round — and the word is not
   inside the picture precisely so that turning the fish never turns the word. */
.fish-img.mirrored { transform: scaleX(-1); }

.fish-cat {
  position: absolute;
  transform: translate(-50%, -50%);
  font-size: 10px; font-weight: 400; letter-spacing: 0.18em;
  text-transform: lowercase;
  color: var(--paper);
  white-space: nowrap;
  pointer-events: none;
}

/* From fully off one side to fully off the other. Percentages of the fish's own
   width on the left, of the band on the right — which is what makes a fish
   leave completely no matter how wide the screen is. */
@keyframes swim {
  from { left: calc(-1 * var(--w) - 40px); }
  to   { left: calc(100% + 40px); }
}

/* Put away until a fish has somewhere to send you. */


@media (prefers-reduced-motion: reduce) {
  .fish { animation: none; left: calc(var(--y) * 0.6); }
  .fish-in { animation: none; }
}
@media (max-width: 1100px) {
  .sea { display: none; }
}

/* ---- the figure: ten strokes in drawing order, 8s a cycle ---- */

    @keyframes k0 { 0%, 1.94% { stroke-dashoffset: 579; } 4.74%, 100% { stroke-dashoffset: 0; } }
    .omino .k0 { animation: k0 8s cubic-bezier(.42,0,.5,1) infinite; }
    @keyframes k1 { 0%, 6.41% { stroke-dashoffset: 608; } 9.35%, 100% { stroke-dashoffset: 0; } }
    .omino .k1 { animation: k1 8s cubic-bezier(.42,0,.5,1) infinite; }
    @keyframes k2 { 0%, 11.01% { stroke-dashoffset: 25; } 13.51%, 100% { stroke-dashoffset: 0; } }
    .omino .k2 { animation: k2 8s cubic-bezier(.42,0,.5,1) infinite; }
    @keyframes k3 { 0%, 14.40% { stroke-dashoffset: 169; } 16.90%, 100% { stroke-dashoffset: 0; } }
    .omino .k3 { animation: k3 8s cubic-bezier(.42,0,.5,1) infinite; }
    @keyframes k4 { 0%, 18.57% { stroke-dashoffset: 554; } 21.24%, 100% { stroke-dashoffset: 0; } }
    .omino .k4 { animation: k4 8s cubic-bezier(.42,0,.5,1) infinite; }
    @keyframes k5 { 0%, 22.13% { stroke-dashoffset: 977; } 26.85%, 100% { stroke-dashoffset: 0; } }
    .omino .k5 { animation: k5 8s cubic-bezier(.42,0,.5,1) infinite; }
    @keyframes k6 { 0%, 27.74% { stroke-dashoffset: 122; } 30.24%, 100% { stroke-dashoffset: 0; } }
    .omino .k6 { animation: k6 8s cubic-bezier(.42,0,.5,1) infinite; }
    @keyframes k7 { 0%, 31.13% { stroke-dashoffset: 692; } 34.47%, 100% { stroke-dashoffset: 0; } }
    .omino .k7 { animation: k7 8s cubic-bezier(.42,0,.5,1) infinite; }
    @keyframes k8 { 0%, 35.36% { stroke-dashoffset: 691; } 38.70%, 100% { stroke-dashoffset: 0; } }
    .omino .k8 { animation: k8 8s cubic-bezier(.42,0,.5,1) infinite; }
    @keyframes k9 { 0%, 39.59% { stroke-dashoffset: 504; } 42.09%, 100% { stroke-dashoffset: 0; } }
    .omino .k9 { animation: k9 8s cubic-bezier(.42,0,.5,1) infinite; }
