/* furniture.html — the Gessi Perle configurator, dressed from gessi.com's
   stylesheet and their configurator's theme, with nothing of the studio's look.

   After common/css/design-system.css, whose `--is-*` tokens it reads, and before
   common/css/loading-screen.css, which the last block dresses by specificity. */

@property --fu-ground-from { syntax: '<color>'; inherits: false; initial-value: #EFECE8; }
@property --fu-ground-to   { syntax: '<color>'; inherits: false; initial-value: #EFECE8; }

:root {
  /* `ges-brown` and its 300, their off-white, and `ges-gray`. */
  --fu-ink:        #14120E;
  --fu-ink-soft:   #544F44;
  --fu-paper:      #FCFBFA;
  --fu-grey:       #EFECE8;

  --fu-sans: 'DM Sans', system-ui, sans-serif;
  --fu-mono: 'DM Mono', ui-monospace, monospace;

  /* Their scale, `2XS` to `M`. */
  --fu-2xs: 0.5rem;
  --fu-xs:  1rem;
  --fu-s:   1.5rem;
  --fu-m:   2rem;

  --fu-pill:   5rem;
  --fu-radius: 0.5rem;
  /* The one curve their configurator animates everything on. */
  --fu-ease:   cubic-bezier(0.25, 0.8, 0.25, 1);

  --fu-column-w: clamp(340px, 26vw, 400px);
}

*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

html, body {
  height: 100%;
  background: var(--fu-grey);
  color: var(--fu-ink);
  font-family: var(--fu-sans);
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
}

button { font: inherit; color: inherit; }

/* Fixed, inset by the studio frame's height above and the configurator column
   beside: the frame does not fold (common/docs/025), and the column is the
   page's ground rather than a panel over the room. */
#fu-stage {
  position: fixed;
  inset: var(--is-topbar-h) var(--fu-column-w) 0 0;
  background: var(--fu-ink);
}

.fu-canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* ── Grounds ──────────────────────────────────────────────────────────────
   Each Finitura's gradient pair, set by controls.js with the tone its ink
   follows. Registered, so a change of finish crossfades rather than cuts. */
[data-tone="light"] {
  --fu-on:       var(--fu-ink);
  --fu-on-soft:  var(--fu-ink-soft);
  --fu-rule:     rgba(20, 18, 14, 0.16);
  --fu-fill:     var(--fu-ink);
  --fu-on-fill:  var(--fu-paper);
}

[data-tone="dark"] {
  --fu-on:       var(--fu-paper);
  --fu-on-soft:  rgba(252, 251, 250, 0.7);
  --fu-rule:     rgba(252, 251, 250, 0.22);
  --fu-fill:     var(--fu-paper);
  --fu-on-fill:  var(--fu-ink);
}

.fu-column,
.fu-perla {
  background: linear-gradient(180deg, var(--fu-ground-from), var(--fu-ground-to));
  color: var(--fu-on);
  transition:
    --fu-ground-from 700ms var(--fu-ease),
    --fu-ground-to 700ms var(--fu-ease),
    color 700ms var(--fu-ease);
}

/* ── The configurator column ─────────────────────────────────────────── */
.fu-column {
  position: fixed;
  top: var(--is-topbar-h);
  right: 0;
  bottom: 0;
  z-index: 10;
  width: var(--fu-column-w);
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  padding: var(--fu-xs) var(--fu-s);
  overflow-y: auto;
  scrollbar-width: thin;
}

.fu-brand {
  display: flex;
  align-items: center;
}

/* Gessi's marks as masks, so each is drawn in the ink of its ground; the
   loading screen's two are these too. */
.fu-mark,
body > .loading-screen .loading-screen__inner::before,
body > .loading-screen::after {
  display: block;
  background: currentColor;
  -webkit-mask: var(--fu-mark) center / contain no-repeat;
  mask: var(--fu-mark) center / contain no-repeat;
}

.fu-mark--gessi {
  --fu-mark: url('/furniture/assets/gessi/brand/gessi.svg');
  width: 2.75rem;
  aspect-ratio: 88 / 48;
}

.fu-mark--perle {
  --fu-mark: url('/furniture/assets/gessi/brand/perle.svg');
  height: 1.375rem;
  aspect-ratio: 102.6 / 24;
}

/* Their step heading: "Scegli la" small, over the word in their h3. */
.fu-step__title {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.125rem;
  margin-bottom: 0.75rem;
  font-weight: 500;
}

.fu-step__lead {
  font-size: 0.875rem;
  font-weight: 400;
  color: var(--fu-on-soft);
}

.fu-step__word {
  font-size: 1.375rem;
  line-height: 1.625rem;
  letter-spacing: -0.03rem;
}

.fu-step__title .fu-mark--perle { margin-top: 0.25rem; }

/* The line under a step: what is chosen, or what the pointer is over. */
.fu-caption {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--fu-xs);
  min-height: 2rem;
  margin-top: var(--fu-2xs);
  padding-top: var(--fu-2xs);
  border-top: 1px solid var(--fu-rule);
  font-size: 0.875rem;
  line-height: 1.25rem;
}

.fu-caption__name { font-weight: 500; }

.fu-caption__code {
  flex: none;
  font-family: var(--fu-mono);
  font-size: 0.75rem;
  color: var(--fu-on-soft);
}

/* ── The two pieces, as a range ───────────────────────────────────────── */
.fu-pieces {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--fu-2xs);
}

.fu-piece {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.375rem;
  padding: 0.75rem 0.875rem;
  border: 1px solid var(--fu-rule);
  border-radius: var(--fu-radius);
  background: transparent;
  text-align: left;
  cursor: pointer;
}

.fu-piece__name {
  font-size: 1rem;
  font-weight: 500;
  line-height: 1.25rem;
  letter-spacing: -0.01rem;
}

.fu-piece__article {
  font-family: var(--fu-mono);
  font-size: 0.6875rem;
  color: var(--fu-on-soft);
}

.fu-piece[aria-pressed="true"] .fu-piece__article { color: inherit; opacity: 0.7; }

.fu-piece:disabled { cursor: progress; }
.fu-piece:disabled:not([aria-pressed="true"], [data-building]) { opacity: 0.5; }

/* The piece being built, while the other stays on screen. */
.fu-piece[data-building] { border-color: var(--fu-on); }
.fu-piece[data-building]::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 40%;
  height: 2px;
  background: currentColor;
  animation: fu-sweep 1.2s ease-in-out infinite;
}

@keyframes fu-sweep {
  from { transform: translateX(-100%); }
  to   { transform: translateX(250%); }
}

/* ── Finitura: their round thumbnails ────────────────────────────────── */
.fu-swatches {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 0.125rem 0;
  justify-items: center;
}

.fu-swatch {
  width: 2.5rem;
  height: 2.5rem;
  padding: 3px;
  border: 1px solid transparent;
  border-radius: 50%;
  background: none;
  cursor: pointer;
}

.fu-swatch img {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
  box-shadow: 0 0 0 1px rgba(20, 18, 14, 0.12);
}

/* ── The Perla, on the picker's pair ──────────────────────────────────── */
.fu-perla {
  padding: var(--fu-xs) var(--fu-xs) 0.75rem;
  border-radius: var(--fu-radius);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12), 0 2px 20px rgba(0, 0, 0, 0.2);
}

.fu-perla__rows {
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
}

.fu-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
}

.fu-row__label {
  flex-basis: 100%;
  font-size: 0.75rem;
  color: var(--fu-on-soft);
}

.fu-pill {
  padding: 0.4375rem 0.75rem;
  border: 1px solid var(--fu-rule);
  border-radius: var(--fu-pill);
  background: transparent;
  font-size: 0.8125rem;
  font-weight: 500;
  line-height: 1rem;
  white-space: nowrap;
  cursor: pointer;
}

/* The two kinds of choice: a piece or a pill fills when chosen, and a
   thumbnail is ringed and grows a little under the pointer, as theirs do. */
.fu-piece,
.fu-pill {
  transition: background-color 300ms var(--fu-ease), border-color 300ms var(--fu-ease), color 300ms var(--fu-ease);
}

.fu-piece:hover:not(:disabled),
.fu-pill:hover:not(:disabled) { border-color: var(--fu-on); }

.fu-piece[aria-pressed="true"],
.fu-pill[aria-pressed="true"] {
  border-color: var(--fu-fill);
  background: var(--fu-fill);
  color: var(--fu-on-fill);
}

.fu-swatch,
.fu-leaf__thumb { transition: border-color 300ms var(--fu-ease), transform 300ms var(--fu-ease); }

.fu-swatch:hover:not(:disabled),
.fu-leaf:hover:not(:disabled) .fu-leaf__thumb { transform: scale(1.05); }

.fu-swatch[aria-pressed="true"],
.fu-leaf[aria-pressed="true"] .fu-leaf__thumb { border-color: var(--fu-on); }

.fu-swatch:disabled,
.fu-leaf:disabled { cursor: progress; }

/* Not offered: in view, and plainly out of reach. The title says why. */
.fu-pill:disabled:not([aria-pressed="true"]) {
  opacity: 0.38;
  cursor: not-allowed;
}

.fu-finitura-perla {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.fu-leaves {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(4.5rem, 1fr));
  gap: 0.625rem 0.25rem;
  --fu-leaf: 3.125rem;
}

/* A Finitura perla: its thumbnail, ringed when chosen, over its name. */
.fu-leaf {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  padding: 0;
  border: none;
  background: none;
  color: inherit;
  cursor: pointer;
}

.fu-leaf__thumb {
  position: relative;
  display: grid;
  place-items: center;
  width: var(--fu-leaf);
  aspect-ratio: 1;
  border: 1px solid transparent;
  border-radius: var(--fu-radius);
}

.fu-leaf__name {
  font-size: 0.6875rem;
  line-height: 0.875rem;
  text-align: center;
  color: var(--fu-on-soft);
}

.fu-leaf[aria-pressed="true"] .fu-leaf__name { color: var(--fu-on); }

/* A screen under 820px tall keeps the whole column in view. After the rules it overrides. */
@media (max-height: 820px) {
  .fu-column { gap: 1rem; }
  .fu-step__title { margin-bottom: 0.5rem; }
  .fu-leaves { --fu-leaf: 2.5rem; row-gap: 0.375rem; }
}

.fu-leaf img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* A Metallo Perla has no render of its own: the Finitura's square, turned
   into a sphere by a highlight, or a Semiperla's bowl by cutting it in half,
   as large as Gessi's renders and, for the bowl, where theirs sits. */
.fu-leaf img[data-wears-finitura],
.fu-leaf__thumb:has(img[data-wears-finitura])::after {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
}

.fu-leaf__thumb:has(img[data-wears-finitura])::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(circle at 34% 30%, rgba(255, 255, 255, 0.55), rgba(255, 255, 255, 0) 42%),
    radial-gradient(circle at 50% 50%, rgba(0, 0, 0, 0) 55%, rgba(0, 0, 0, 0.4) 100%);
  pointer-events: none;
}

.fu-leaf[data-forma="semiperla"] img[data-wears-finitura],
.fu-leaf[data-forma="semiperla"] .fu-leaf__thumb:has(img[data-wears-finitura])::after {
  clip-path: inset(50% 0 0 0);
  translate: 0 -25%;
}

/* ── Over the room ────────────────────────────────────────────────────────
   The Viewpoints down the room's left edge, centred on it, as their
   configurator's dark round buttons: black at half, the same two shadows. */
.fu-viewpoints {
  position: fixed;
  left: var(--fu-s);
  top: calc(var(--is-topbar-h) + (100vh - var(--is-topbar-h)) / 2);
  z-index: 10;
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
  transform: translateY(-50%);
}

.fu-viewpoint {
  position: relative;
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  border: 0;
  border-radius: 50%;
  background: rgba(20, 18, 14, 0.5);
  box-shadow: 0 1px 8px rgba(0, 0, 0, 0.12), 0 1px 4px rgba(0, 0, 0, 0.24);
  color: var(--fu-paper);
  cursor: pointer;
  transition:
    background-color 300ms var(--fu-ease),
    color 300ms var(--fu-ease),
    box-shadow 300ms var(--fu-ease),
    transform 300ms var(--fu-ease);
}

.fu-viewpoint svg {
  width: 1.5rem;
  height: 1.5rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.25;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.fu-viewpoint:hover {
  background: rgba(20, 18, 14, 1);
  box-shadow: 0 7px 14px rgba(0, 0, 0, 0.25), 0 5px 5px rgba(0, 0, 0, 0.22);
  transform: scale(1.05);
}

.fu-viewpoint[aria-pressed="true"] {
  background: var(--fu-paper);
  color: var(--fu-ink);
}

.fu-viewpoint:focus-visible {
  outline: 2px solid var(--fu-paper);
  outline-offset: 3px;
}

/* The Viewpoint's name, beside its icon while the pointer or the focus is on it. */
.fu-viewpoint__name {
  position: absolute;
  left: calc(100% + 0.625rem);
  top: 50%;
  padding: 0.3125rem 0.75rem;
  border-radius: var(--fu-pill);
  background: rgba(20, 18, 14, 0.72);
  color: var(--fu-paper);
  font-size: 0.75rem;
  font-weight: 500;
  white-space: nowrap;
  opacity: 0;
  transform: translate(-0.25rem, -50%);
  transition: opacity 200ms var(--fu-ease), transform 200ms var(--fu-ease);
  pointer-events: none;
}

.fu-viewpoint:hover .fu-viewpoint__name,
.fu-viewpoint:focus-visible .fu-viewpoint__name {
  opacity: 1;
  transform: translate(0, -50%);
}

/* Drag to rotate, and say so once: nothing else on screen suggests the room
   moves. Bottom centre of the room, on a dark a white basin cannot swallow. */
.fu-hint {
  position: fixed;
  bottom: var(--fu-s);
  left: calc((100vw - var(--fu-column-w)) / 2);
  z-index: 10;
  padding: 0.25rem 0.75rem;
  border-radius: var(--fu-pill);
  background: rgba(20, 18, 14, 0.36);
  transform: translateX(-50%);
  font-size: 0.6875rem;
  color: rgba(252, 251, 250, 0.9);
  white-space: nowrap;
  pointer-events: none;
}

/* ── The Consulente ───────────────────────────────────────────────────────
   One line at the room's bottom right, beside the column rather than over it,
   in the Viewpoints' dark rounds: the pill holds the corner and the rounds grow
   leftward from it. The transcript rises above on their paper, as the Perla's
   card sits on the column. */
.fu-voice {
  position: fixed;
  right: calc(var(--fu-column-w) + var(--fu-s));
  bottom: var(--fu-s);
  z-index: 20;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.625rem;
}

.fu-voice__row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.fu-voice__pill,
.fu-voice__round {
  height: 3rem;
  border: 0;
  border-radius: var(--fu-pill);
  background: rgba(20, 18, 14, 0.5);
  box-shadow: 0 1px 8px rgba(0, 0, 0, 0.12), 0 1px 4px rgba(0, 0, 0, 0.24);
  color: var(--fu-paper);
  cursor: pointer;
  transition:
    background-color 300ms var(--fu-ease),
    color 300ms var(--fu-ease),
    box-shadow 300ms var(--fu-ease),
    opacity 300ms var(--fu-ease);
}

.fu-voice__pill:hover:not(:disabled),
.fu-voice__round:hover {
  background: rgba(20, 18, 14, 1);
  box-shadow: 0 7px 14px rgba(0, 0, 0, 0.25), 0 5px 5px rgba(0, 0, 0, 0.22);
}

.fu-voice__pill:focus-visible,
.fu-voice__round:focus-visible {
  outline: 2px solid var(--fu-paper);
  outline-offset: 3px;
}

.fu-voice__pill {
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0 1.25rem 0 1.125rem;
  font-size: 0.875rem;
  font-weight: 500;
  white-space: nowrap;
}

/* Every label in one cell, only the current one seen: the pill is as wide as the
   longest in every state, so a turn changing never moves the buttons beside it. */
.fu-voice__labels {
  display: grid;
  text-align: start;
}

.fu-voice__labels > span {
  grid-area: 1 / 1;
  visibility: hidden;
}

.fu-voice__labels > [data-shown] { visibility: visible; }

.fu-voice__pill:disabled { cursor: progress; }
.fu-voice__pill[data-state="unavailable"] { cursor: not-allowed; opacity: 0.6; }

.fu-voice__round {
  display: grid;
  place-items: center;
  width: 3rem;
  padding: 0;
}

.fu-voice__round[hidden] { display: none; }

.fu-voice__round svg {
  width: 1.375rem;
  height: 1.375rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.25;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Muted reads as pressed, as a chosen Viewpoint does, and its microphone is struck:
   the one place the dock says so. */
.fu-voice__mute[aria-pressed="true"] {
  background: var(--fu-paper);
  color: var(--fu-ink);
}

.fu-voice__mute .fu-voice__off,
.fu-voice__mute[aria-pressed="true"] .fu-voice__on { display: none; }
.fu-voice__mute[aria-pressed="true"] .fu-voice__off { display: block; }

/* The dot is the state: breathing while it listens, beating while it connects,
   swelling while it speaks. */
.fu-voice__dot {
  flex: none;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: currentColor;
}

[data-state="listening"] > .fu-voice__dot { animation: fu-voice-breathe 1.8s ease-in-out infinite; }
[data-state="connecting"] > .fu-voice__dot { animation: fu-voice-beat 900ms ease-in-out infinite; }
[data-state="speaking"] > .fu-voice__dot { animation: fu-voice-swell 520ms ease-in-out infinite alternate; }

@keyframes fu-voice-breathe {
  0%, 100% { transform: scale(1); opacity: 1; }
  50%      { transform: scale(1.5); opacity: 0.55; }
}

@keyframes fu-voice-beat {
  0%, 100% { opacity: 0.25; }
  50%      { opacity: 1; }
}

@keyframes fu-voice-swell {
  from { transform: scale(1); }
  to   { transform: scale(1.8); }
}

.fu-voice__sheet {
  display: flex;
  flex-direction: column;
  width: min(22rem, calc(100vw - var(--fu-column-w) - 2 * var(--fu-s)));
  max-height: min(26rem, calc(100vh - var(--is-topbar-h) - 7rem));
  overflow: hidden;
  border-radius: var(--fu-radius);
  background: var(--fu-paper);
  color: var(--fu-ink);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12), 0 2px 20px rgba(0, 0, 0, 0.2);
}

.fu-voice__sheet[hidden],
.fu-voice__write[hidden] { display: none; }

.fu-voice__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.625rem 0.625rem 0.5rem var(--fu-xs);
  border-bottom: 1px solid rgba(20, 18, 14, 0.12);
  font-size: 0.75rem;
  color: var(--fu-ink-soft);
}

.fu-voice__close {
  display: grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  border: 0;
  border-radius: 50%;
  background: none;
  cursor: pointer;
}

.fu-voice__close:hover { background: var(--fu-grey); }

.fu-voice__close svg {
  width: 1rem;
  height: 1rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
}

.fu-voice__lines {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
  min-height: 3rem;
  padding: var(--fu-xs);
  overflow-y: auto;
  scrollbar-width: thin;
}

.fu-voice__line {
  max-width: 85%;
  font-size: 0.875rem;
  line-height: 1.375rem;
}

.fu-voice__line[data-role="agent"] { align-self: flex-start; }

.fu-voice__line[data-role="user"] {
  align-self: flex-end;
  padding: 0.375rem 0.75rem;
  border-radius: var(--fu-radius);
  background: var(--fu-grey);
}

.fu-voice__line[data-role="note"] {
  align-self: stretch;
  max-width: none;
  font-size: 0.8125rem;
  color: var(--fu-ink-soft);
}

.fu-voice__write {
  display: flex;
  gap: 0.5rem;
  padding: 0.625rem var(--fu-xs) var(--fu-xs);
  border-top: 1px solid rgba(20, 18, 14, 0.12);
}

.fu-voice__field {
  flex: 1;
  min-width: 0;
  padding: 0.5rem 0.875rem;
  border: 1px solid rgba(20, 18, 14, 0.16);
  border-radius: var(--fu-pill);
  background: transparent;
  font: inherit;
  font-size: 0.875rem;
  color: inherit;
}

.fu-voice__field:focus {
  outline: none;
  border-color: var(--fu-ink);
}

.fu-voice__send {
  padding: 0 1rem;
  border: 1px solid var(--fu-ink);
  border-radius: var(--fu-pill);
  background: var(--fu-ink);
  color: var(--fu-paper);
  font-size: 0.8125rem;
  font-weight: 500;
  cursor: pointer;
}

/* The invitation, centred on the room rather than the window. Only the card
   takes the pointer, so a drag on the room goes through and dismisses it. */
.fu-invite {
  position: fixed;
  inset: var(--is-topbar-h) var(--fu-column-w) 0 0;
  z-index: 30;
  display: grid;
  place-items: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 450ms var(--fu-ease);
}

.fu-invite[data-open="true"] { opacity: 1; }

.fu-invite__card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  padding: var(--fu-s) var(--fu-m) 1.375rem;
  border: 0;
  border-radius: var(--fu-radius);
  background: var(--fu-paper);
  color: var(--fu-ink);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12), 0 2px 20px rgba(0, 0, 0, 0.2);
  text-align: center;
  cursor: pointer;
  transform: translateY(6px) scale(0.98);
  transition: transform 450ms var(--fu-ease), box-shadow 300ms var(--fu-ease);
}

.fu-invite[data-open="true"] .fu-invite__card {
  pointer-events: auto;
  transform: none;
}

.fu-invite__card:hover {
  box-shadow: 0 7px 14px rgba(0, 0, 0, 0.25), 0 5px 5px rgba(0, 0, 0, 0.22), 0 2px 20px rgba(0, 0, 0, 0.2);
}

.fu-invite__rings {
  position: relative;
  display: grid;
  place-items: center;
  width: 4.5rem;
  height: 4.5rem;
  margin-bottom: 0.25rem;
}

.fu-invite__ring {
  position: absolute;
  border: 1px solid var(--fu-ink);
  border-radius: 50%;
  animation: fu-invite-ping 2.2s ease-out infinite;
}

.fu-invite__ring + .fu-invite__ring { animation-delay: 1.1s; }

@keyframes fu-invite-ping {
  from { width: 3rem; height: 3rem; opacity: 0.5; }
  to   { width: 6.5rem; height: 6.5rem; opacity: 0; }
}

.fu-invite__badge {
  position: relative;
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  background: var(--fu-ink);
  color: var(--fu-paper);
}

.fu-invite__badge svg {
  width: 1.375rem;
  height: 1.375rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.25;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Their step heading, centred, off the column whose tone it would read. */
.fu-invite__title {
  align-items: center;
  margin: 0;
}

.fu-invite__title .fu-step__lead { color: var(--fu-ink-soft); }

.fu-invite__sub {
  max-width: 20rem;
  text-wrap: balance;
  font-size: 0.875rem;
  line-height: 1.25rem;
  color: var(--fu-ink-soft);
}

@media (prefers-reduced-motion: reduce) {
  .fu-voice__dot,
  .fu-invite__ring { animation: none; }
}

/* ── The developer's readout ──────────────────────────────────────────────
   Deliberately not dressed as part of the experience: this is an instrument,
   legible over a bright bathroom, and it is only there under `?dev`. Top
   left, clear of the Viewpoints and of lil-gui beside the column. */
.fu-stats {
  position: fixed;
  top: calc(var(--is-topbar-h) + 12px);
  left: 12px;
  z-index: 10;
  min-width: 190px;
  padding: 10px 12px;
  border-radius: 8px;
  background: rgba(20, 18, 14, 0.72);
  font-family: var(--fu-mono);
  font-variant-numeric: tabular-nums;
  font-size: 11px;
  line-height: 1.55;
  color: rgba(252, 251, 250, 0.6);
  pointer-events: none;
  user-select: none;
}

.fu-stats b { color: var(--fu-paper); font-weight: 400; }

/* The two readings that answer "is this fast enough", set apart from the
   counts that explain them. */
.fu-stats__rate,
.fu-stats__gpu {
  font-size: 13px;
}

.fu-stats__gpu {
  padding-bottom: 4px;
  margin-bottom: 4px;
  border-bottom: 1px solid rgba(252, 251, 250, 0.14);
}

.fu-stats__note {
  font-size: 10px;
  color: rgba(252, 251, 250, 0.38);
}

/* ── The lighting panel ───────────────────────────────────────────────────
   lil-gui, mounted only under `?dev`. It autoplaces at `top: 0`, under the
   studio frame, which does not fold (common/docs/025). z-index 3100 clears the
   frame at 3000 and stays under the loading screen at 9000; lil-gui's own
   `z-index: 1001` is hard-coded, so it is restated rather than inherited.
   Beside the configurator column, and leashed to the viewport because it is
   taller than it looks once opened. */
.fu-gui.lil-gui.autoPlace {
  z-index: 3100;
  top: calc(var(--is-topbar-h) + 12px);
  right: calc(var(--fu-column-w) + 12px);
  max-height: calc(100vh - var(--is-topbar-h) - 24px);
  overflow-y: auto;
}

/* ── The studio's loading screen, in Gessi's dress ────────────────────────
   Gessi's marks over their configurator's bar, the studio's words kept for a
   screen reader. Each rule is the shared selector behind `body >`, one element
   heavier, because loading-screen.css comes later. */
body > .loading-screen {
  background: var(--fu-grey);
  color: var(--fu-ink);
  font-family: var(--fu-sans);
}

body > .loading-screen .loading-screen__section,
body > .loading-screen .loading-screen__title,
body > .loading-screen .loading-screen__brand {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

body > .loading-screen .loading-screen__inner::before {
  --fu-mark: url('/furniture/assets/gessi/brand/perle.svg');
  content: '';
  width: 13rem;
  aspect-ratio: 102.6 / 24;
  margin-bottom: var(--fu-m);
}

body > .loading-screen::after {
  --fu-mark: url('/furniture/assets/gessi/brand/gessi.svg');
  content: '';
  position: absolute;
  left: 50%;
  bottom: var(--fu-m);
  width: 3rem;
  aspect-ratio: 88 / 48;
  transform: translateX(-50%);
}

body > .loading-screen .loading-screen__track {
  width: 13rem;
  height: 6px;
  border: 1px solid var(--fu-ink);
  border-radius: 0;
  background: transparent;
}

body > .loading-screen .loading-screen__bar {
  border-radius: 0;
  background: var(--fu-ink);
}

body > .loading-screen .loading-screen__note {
  max-width: 22rem;
  font-size: 0.875rem;
  color: var(--fu-ink-soft);
}

body > .loading-screen[data-state="failed"] .loading-screen__note { color: var(--fu-ink); }
