/* ============================================================
   THE DESK — page styling
   The 3D fills the window; the copy sits over it. Everything here
   is layout and type — nothing about the scene is set from CSS.
   ============================================================ */
* { margin: 0; box-sizing: border-box; }
html, body { height: 100%; overflow: hidden; }
body { background: #31536C; color: #F4F0E8;
       font-family: 'Manrope', system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
#stage { position: fixed; inset: 0; }
#stage canvas { display: block; }

/* The left third is the copy column; the camera is panned so the desk keeps
   to the right two thirds. The width is held to the column rather than the
   text's own measure, so the two never overlap at any window size. */
.copy { position: fixed; left: clamp(22px, 5vw, 72px);
        top: 50%; transform: translateY(-50%);
        width: calc(33.333vw - clamp(22px, 5vw, 72px) - 24px);
        max-width: 26rem; z-index: 2; pointer-events: none; }
.copy h1, .copy p, .hint { transition: color 0.5s ease; }
body.night .copy h1 { color: #F4F0E8; }
body.night .copy p  { color: rgba(244, 240, 232, 0.62); }
body.night .hint    { color: rgba(244, 240, 232, 0.42); }

/* Bone, not ink. The wall behind the headline is concrete now and the dark
   type had nothing to sit against; bone reads on both the wall and the red
   floor the lower half of the block crosses. */
.copy h1 { color: #F4F0E8;
           text-shadow: 0 2px 18px rgba(12, 4, 4, 0.45);
           font-family: 'Onest', system-ui, sans-serif; font-weight: 500;
           font-size: clamp(1.4rem, 2.6vw, 2.6rem); line-height: 1.14;
           letter-spacing: -0.03em; }
.copy p { margin-top: 16px; font-size: 0.9rem; line-height: 1.55;
          color: rgba(244, 240, 232, 0.68);
          text-shadow: 0 1px 14px rgba(12, 4, 4, 0.4); }

/* Too narrow for two columns. The scene stops panning at the same ratio
   (see panBy in scene.js) and the copy sits along the bottom instead. */
@media (max-aspect-ratio: 115/100) {
  .copy { top: auto; bottom: clamp(56px, 9vh, 96px); transform: none;
          width: auto; right: clamp(22px, 5vw, 72px); max-width: 30rem; }
}

/* ---------- the Enter key on the page ----------
   The same object as the Enter on the keyboard overlay, built the same way:
   a run of hard box-shadows at one-pixel steps, which stack into a solid
   side wall. `.copy` above it takes no pointer events, so this has to ask
   for them back. */
.go-wrap { margin-top: 30px; pointer-events: auto; perspective: 700px; }

/* White, not red. Red on a red floor was a key you had to look for; bone is
   the one value nothing else in the frame is wearing. */
.go-key {
  border: 0; cursor: pointer; font: inherit;
  padding: 18px 38px; border-radius: 7px 7px 9px 9px;
  background: linear-gradient(180deg, #FFFFFF 0%, #F7F3EA 46%, #E9E3D6 100%);
  color: #17150F; font-size: 0.78rem; font-weight: 500;
  letter-spacing: 0.2em; text-transform: uppercase;
  transform: rotateX(13deg);
  transition: transform 0.09s ease, box-shadow 0.09s ease, background 0.2s ease;
  animation: goPulse 2.4s ease-in-out infinite;
}
.go-key:hover {
  background: linear-gradient(180deg, #FFFFFF, #FFFDF8 46%, #F2ECE0 100%);
  color: #C42024;
}
.go-key:active {
  transform: rotateX(13deg) translateY(5px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9),
              0 1px 0 #B4AC9C, 0 2px 5px rgba(60, 12, 12, 0.4);
  animation: none;
}
@keyframes goPulse {
  0%, 100% {
    box-shadow:
      inset 0 1px 0 rgba(255, 255, 255, 0.95),
      inset 0 -2px 4px rgba(120, 104, 80, 0.22),
      0 1px 0 #DFD8C9, 0 2px 0 #D3CBBA, 0 3px 0 #C7BEAB, 0 4px 0 #BBB19C,
      0 5px 0 #AFA48D, 0 6px 0 #A3977F,
      0 12px 20px rgba(60, 10, 10, 0.35), 0 0 0 0 rgba(255, 255, 255, 0.55);
  }
  55% {
    box-shadow:
      inset 0 1px 0 rgba(255, 255, 255, 0.95),
      inset 0 -2px 4px rgba(120, 104, 80, 0.22),
      0 1px 0 #DFD8C9, 0 2px 0 #D3CBBA, 0 3px 0 #C7BEAB, 0 4px 0 #BBB19C,
      0 5px 0 #AFA48D, 0 6px 0 #A3977F,
      0 12px 20px rgba(60, 10, 10, 0.35), 0 0 0 18px rgba(255, 255, 255, 0);
  }
}

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

#flash { position: fixed; inset: 0; z-index: 9; background: #F4F0E8;
          opacity: 0; pointer-events: none; }

/* ============================================================
   THE CONTACT CARD — opened by the telephone

   The paper is built rather than loaded: two crossing linear gradients
   for the laid lines, a radial for the light falling across it, and an
   SVG turbulence data-URI for the tooth. No image request, and it stays
   sharp at any size.
   ============================================================ */
.card-wrap { position: fixed; inset: 0; z-index: 20; display: grid; place-items: center;
             padding: 24px; }
.card-wrap[hidden] { display: none; }

.card-back { position: absolute; inset: 0; background: rgba(12, 8, 8, 0.5);
             backdrop-filter: blur(3px); opacity: 0; transition: opacity 0.4s ease; }
.card-wrap.open .card-back { opacity: 1; }

.card {
  position: relative; width: min(40rem, 100%);
  padding: clamp(26px, 3.4vw, 44px);
  /* Flat stock and a square edge. The tooth, the wash and the torn outline
     were doing an impression of paper; the layout does the work instead —
     a red kicker over a heavy rule, and display type set large and tight. */
  background: #FBF9F5;
  color: #17150F;
  border-top: 7px solid #C42024;
  box-shadow: 0 30px 70px rgba(30, 12, 10, 0.45);

  /* It flies out of the telephone. scene.js projects the phone's world
     position to the screen each time and writes the offset into --fx/--fy,
     so the card leaves from wherever the phone happens to be under the
     turntable — not from a spot hard-coded here. */
  --fx: 0px; --fy: 0px;
  transform-origin: 50% 50%;
  opacity: 0; transform: translate(var(--fx), var(--fy)) scale(0.04) rotate(-200deg);
  transition: opacity 0.3s ease, transform 0.78s cubic-bezier(0.17, 0.86, 0.28, 1);
}
/* Square. The half-degree lean was meant to read as a card dropped on a
   desk; on a form with ruled fields it just reads as a crooked dialog. */
.card-wrap.open .card { opacity: 1; transform: translate(0, 0) scale(1) rotate(0deg); }

.card-eyebrow { font-size: 0.58rem; letter-spacing: 0.3em; text-transform: uppercase;
                font-weight: 500; color: #C42024;
                padding-bottom: 12px; border-bottom: 3px solid #17150F; }
.card h2 { margin-top: 20px; font-family: 'Onest', system-ui, sans-serif; font-weight: 500;
           font-size: clamp(1.4rem, 3vw, 2rem); line-height: 1.06;
           letter-spacing: -0.042em; color: #17150F; }

/* ---------- the form: ruled lines on paper, not boxes ---------- */
.card-form { margin-top: 18px; }
/* name and email share a line — the card is wide enough, and it is what
   keeps the whole thing short enough to need no scrollbar */
.card-row { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
@media (max-width: 34rem) { .card-row { grid-template-columns: 1fr; gap: 0; } }
.card-form label { display: block; margin-bottom: 11px; }
.card-form span { display: block; font-size: 0.6rem; letter-spacing: 0.24em;
                  text-transform: uppercase; font-weight: 500;
                  color: #C42024; margin-bottom: 5px; }
.card-form input, .card-form textarea {
  width: 100%; padding: 6px 2px; border: 0;
  border-bottom: 2px solid rgba(23, 21, 15, 0.45);
  background: transparent; color: #17150F; font: inherit; font-size: 0.95rem;
  border-radius: 0; resize: vertical;
}
.card-form input:focus, .card-form textarea:focus {
  outline: none; border-bottom-color: #C42024;
}
.card-form input:user-invalid, .card-form textarea:user-invalid {
  border-bottom-color: rgba(196, 32, 36, 0.55);
}
.card-form button {
  margin-top: 10px; padding: 13px 30px; border: 0; border-radius: 0;
  background: #C42024; color: #FBF9F5; font: inherit; font-size: 0.72rem;
  font-weight: 500; letter-spacing: 0.2em; text-transform: uppercase;
  cursor: pointer; transition: background 0.2s ease;
}
.card-form button:hover { background: #17150F; }
.card-note { margin-top: 10px; min-height: 1.1em; font-size: 0.76rem;
             color: rgba(23, 21, 15, 0.78); }
.card-note.bad { color: #C42024; }

.card-foot { margin-top: 20px; padding-top: 13px; font-size: 0.64rem;
             letter-spacing: 0.14em; text-transform: uppercase;
             border-top: 3px solid #17150F;
             color: rgba(23, 21, 15, 0.6); }
.card-foot a { color: #17150F; font-weight: 500; text-decoration: none; }
.card-foot a:hover { color: #C42024; }
.card-foot span { margin: 0 6px; }

.card-x { position: absolute; top: 12px; right: 14px; width: 34px; height: 34px;
          border: 0; background: none; cursor: pointer; font-size: 1.5rem; line-height: 1;
          color: rgba(23, 21, 15, 0.65); font-family: inherit; }
.card-x:hover { color: #C42024; }

/* ============================================================
   THE DRAWER'S CONTENTS

   Opens and shuts with the top drawer. The tiles do not fade in
   together — each carries its own index and comes up a beat after
   the one before, which is what makes it read as a set landing
   rather than a panel appearing.
   ============================================================ */
.apps-wrap { position: fixed; inset: 0; z-index: 18; display: grid; place-items: center;
             padding: 24px; }
.apps-wrap[hidden] { display: none; }

.apps-back { position: absolute; inset: 0; background: rgba(10, 6, 6, 0.62);
             backdrop-filter: blur(4px); opacity: 0; transition: opacity 0.4s ease; }
.apps-wrap.open .apps-back { opacity: 1; }

.apps { position: relative; width: min(44rem, 100%); color: #F4F0E8;
        text-align: center; }
.apps-eyebrow { font-size: 0.63rem; letter-spacing: 0.24em; text-transform: uppercase;
                color: rgba(244, 240, 232, 0.45); }
.apps h2 { margin-top: 8px; margin-bottom: 26px;
           font-family: 'Onest', system-ui, sans-serif; font-weight: 500;
           font-size: clamp(1.3rem, 3vw, 1.9rem); letter-spacing: -0.025em; }

.apps-grid { display: grid; grid-template-columns: repeat(6, max-content);
             justify-content: center; list-style: none; padding: 0;
             gap: clamp(20px, 3vw, 38px) clamp(12px, 1.8vw, 26px); }
@media (max-width: 58rem) { .apps-grid { grid-template-columns: repeat(4, max-content); } }
@media (max-width: 34rem) { .apps-grid { grid-template-columns: repeat(3, max-content); } }

/* ---------- one product, and its flight out of the drawer ----------
   scene.js measures each box once the overlay is laid out and writes the
   offset back to the open drawer into --fx/--fy, so every one leaves from
   the drawer and arrives at its own place in the grid. */
.pk {
  --w: clamp(66px, 8vw, 112px);
  --h: calc(var(--w) * 1.34);
  --d: calc(var(--w) * 0.22);
  --fx: 0px; --fy: 0px;
  width: var(--w); perspective: 820px;
  opacity: 0; transform: translate(var(--fx), var(--fy)) scale(0.05) rotate(-120deg);
  transition: opacity 0.32s ease, transform 0.82s cubic-bezier(0.18, 0.88, 0.28, 1);
}
.apps-wrap.open .pk {
  opacity: 1; transform: none;
  transition-delay: calc(var(--i) * 52ms);
}

/* The float is on its own wrapper. A keyframe animation and a transition
   cannot both drive transform on one element — the animation simply wins. */
.pk-float { animation: pkBob 4.9s ease-in-out infinite;
            animation-delay: calc(var(--i) * -0.41s); }
@keyframes pkBob {
  0%, 100% { transform: translateY(0) rotate(-0.7deg); }
  50%      { transform: translateY(-11px) rotate(0.7deg); }
}

.pk-box { position: relative; width: var(--w); height: var(--h);
          transform-style: preserve-3d;
          transform: rotateX(5deg) rotateY(25deg);
          transition: transform 0.45s cubic-bezier(0.2, 0.9, 0.3, 1); }
/* the little movement on hover: it turns to face you and steps forward */
.pk:hover .pk-box { transform: rotateX(1deg) rotateY(11deg) translateZ(26px); }

.pk-front, .pk-side, .pk-top { position: absolute; }

.pk-front {
  width: var(--w); height: var(--h);
  transform: translateZ(calc(var(--d) / 2));
  background: linear-gradient(158deg, #FFFDF7, #E6DFCE 62%, #CFC6B2);
  border: 1px solid rgba(0, 0, 0, 0.16);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6%; padding: 8%;
}
.pk-front b {
  display: grid; place-items: center; width: 46%; aspect-ratio: 1;
  border-radius: 22%; background: var(--bg); color: var(--ink);
  border: 1px solid var(--ink);
  font-family: 'Onest', system-ui, sans-serif; font-weight: 500;
  font-size: clamp(0.62rem, 1.5vw, 1rem); letter-spacing: -0.02em;
}
.pk-front span { font-size: clamp(0.44rem, 0.85vw, 0.62rem); color: #23201B;
                 text-align: center; line-height: 1.2; }
.pk-front i { font-style: normal; font-size: clamp(0.36rem, 0.7vw, 0.5rem);
              letter-spacing: 0.2em; text-transform: uppercase;
              color: rgba(35, 32, 27, 0.45); }

/* The spine sits at the box's left edge: a panel one depth wide, shifted
   back by half of that so its centre lands on the edge, then swung into
   the side plane. */
.pk-side {
  width: var(--d); height: var(--h);
  left: calc(var(--d) / -2);
  transform: rotateY(-90deg);
  background: linear-gradient(180deg, var(--ink), var(--bg));
  display: grid; place-items: center; overflow: hidden;
}
.pk-side u { text-decoration: none; white-space: nowrap;
             transform: rotate(90deg);
             font-size: clamp(0.34rem, 0.66vw, 0.48rem); letter-spacing: 0.1em;
             color: rgba(255, 255, 255, 0.85); }

.pk-top {
  width: var(--w); height: var(--d);
  top: calc(var(--d) / -2);
  transform: rotateX(90deg);
  background: linear-gradient(90deg, #E9E2D1, #CFC6B2);
}

.apps-x { position: absolute; top: -6px; right: 0; width: 34px; height: 34px;
          border: 0; background: none; cursor: pointer; font-size: 1.5rem; line-height: 1;
          color: rgba(244, 240, 232, 0.45); font-family: inherit; }
.apps-x:hover { color: #F4F0E8; }



/* ============================================================
   THE NOTEBOOK

   Arrives shut — bone cover, red spine, the mark — and a tab opens
   it. The cover is hinged on its left edge and carries the
   left-hand page on its own back, so opening is one rotation
   rather than a crossfade between two different objects.

   Both pages are a fixed 400x486. Nothing reflows between
   sections, and a section with two items is exactly the same size
   as one with six. Small windows scale it from notebook.js rather
   than reflowing it.
   ============================================================ */
.book-wrap { position: fixed; inset: 0; z-index: 22; display: grid; place-items: center; }
.book-wrap[hidden] { display: none; }

.book-back { position: absolute; inset: 0; background: rgba(10, 6, 6, 0.66);
             backdrop-filter: blur(5px); opacity: 0; transition: opacity 0.45s ease; }
.book-wrap.open .book-back { opacity: 1; }

.book-fit { transform: scale(var(--fit, 1)); }   /* set in notebook.js */

.book-stage { position: relative; perspective: 2200px;
              opacity: 0; transform: translateY(46px) scale(0.88) rotateX(26deg);
              transform-origin: 50% 100%;
              transition: opacity 0.4s ease, transform 0.7s cubic-bezier(0.18, 0.9, 0.28, 1); }
.book-wrap.open .book-stage { opacity: 1; transform: none; }

/* ---------- the book: two fixed pages, a hinge down the middle ---------- */
.nb {
  --pg: 400px;
  --ph: 486px;
  position: relative; width: calc(var(--pg) * 2); height: var(--ph);
  transform-style: preserve-3d;
  /* shut, only the right half is occupied — shifting half a page left is
     what keeps the closed cover centred in the window */
  transform: translateX(calc(var(--pg) / -2));
  transition: transform 0.85s cubic-bezier(0.25, 0.9, 0.25, 1);
}
.nb.open { transform: translateX(0); }

/* Flat stock, no tooth, no wash. A printed page is a flat sheet of ink on
   white — the noise and the light-falloff gradient were pastiche, and they
   were the reason the type sat in a haze instead of on a page. */
.nb-page, .nb-out { background: #FBF9F5; color: #17150F; }

/* The left page is set in ink, the right on bone: the brand's own two
   values, and the spread now has some weight to it instead of two
   near-identical sheets of off-white. Red carries the kicker on both. */
.nb-in { background: #17150F; color: #FBF9F5; }
.nb-in .book-eyebrow { color: #ED4A44; border-bottom-color: #FBF9F5; }
/* .nb-page.nb-in, not .nb-in: `.nb-page h2` further down carries the same
   specificity and, coming later, was winning — which set the headline in
   black on a black page. */
.nb-page.nb-in h2 { color: #FBF9F5; }
.nb-in .book-intro { color: rgba(251, 249, 245, 0.78); }
.nb-in .book-folio { color: rgba(251, 249, 245, 0.5);
                     border-top-color: rgba(251, 249, 245, 0.28); }

/* extra room on the left: the item numbers hang in that margin */
.nb-right { position: absolute; right: 0; top: 0; width: var(--pg); height: var(--ph);
            padding: 30px 28px 30px 46px; overflow: hidden;
            box-shadow: 0 30px 70px rgba(30, 12, 10, 0.45);
            border-radius: 0 3px 3px 0; }

.nb-cover {
  position: absolute; left: var(--pg); top: 0; width: var(--pg); height: var(--ph);
  transform-origin: left center; transform-style: preserve-3d;
  transform: rotateY(0deg);
  transition: transform 0.9s cubic-bezier(0.3, 0.86, 0.24, 1);
}
.nb.open .nb-cover { transform: rotateY(-172deg); }

.nb-face { position: absolute; inset: 0; backface-visibility: hidden;
           border-radius: 0 3px 3px 0; }
.nb-in { transform: rotateY(180deg); border-radius: 3px 0 0 3px;
         padding: 34px 30px; overflow: hidden; }

/* ---------- the shut cover ----------
   Laid out like a masthead: the mark and wordmark set at the top over a
   heavy rule, the issue line at the foot, and the rest left empty. */
.nb-out { display: flex; flex-direction: column;
          padding: 46px 40px 34px 52px;
          box-shadow: 0 34px 76px rgba(30, 12, 10, 0.5); }
.nb-spine { position: absolute; left: 0; top: 0; bottom: 0; width: 22px;
            background: #C42024; }
.nb-lockup { display: flex; flex-direction: column; align-items: flex-start; gap: 20px;
             padding-bottom: 26px; border-bottom: 4px solid #17150F; }
.nb-lockup svg { width: 52px; height: auto; display: block; }
.nb-lockup .lm-body { fill: #17150F; }
.nb-lockup .lm-tip  { fill: #C42024; }
.nb-lockup em { font-style: normal; font-family: 'Onest', system-ui, sans-serif;
                font-weight: 500; font-size: 1.9rem; line-height: 1.02;
                letter-spacing: -0.042em; color: #17150F; }
.nb-open-hint { margin-top: auto; font-size: 0.56rem;
                letter-spacing: 0.3em; text-transform: uppercase;
                color: #C42024; transition: opacity 0.3s ease; }
.nb.open .nb-open-hint { opacity: 0; }

/* ---------- what is printed on the pages ----------
   Editorial rather than papery: a red kicker over a heavy rule, display type
   set tight and large, and the list numbered down the right-hand page the way
   a contents page runs. The hierarchy does the work the texture was doing. */
/* ---------- the leaf that turns between sections ----------
   Hinged on the gutter and swung over to the left, with the spread swapping
   underneath while it is edge-on. The faces carry the two colours the book
   already uses, so the leaf is a page of this book rather than a prop: bone
   on the way over, ink as it lands on the left.

   The transition lives only in the .turning state. Taken off, the leaf has
   no transition to run and snaps back to its start instantly instead of
   unwinding through 175 degrees in full view. */
.nb-leaf {
  position: absolute; right: 0; top: 0; width: var(--pg); height: var(--ph);
  transform-origin: left center; transform-style: preserve-3d;
  transform: rotateY(0deg); visibility: hidden; pointer-events: none;
}
.nb.turning .nb-leaf {
  visibility: visible; transform: rotateY(-175deg);
  transition: transform 0.62s cubic-bezier(0.4, 0.05, 0.3, 1);
}
.nb-leaf-f, .nb-leaf-b {
  position: absolute; inset: 0; backface-visibility: hidden;
  box-shadow: 0 14px 34px rgba(20, 8, 8, 0.28);
}
.nb-leaf-f { background: #FBF9F5; transform: translateZ(2px);
             border-radius: 0 3px 3px 0; }
.nb-leaf-b { background: #17150F; transform: rotateY(180deg) translateZ(2px);
             border-radius: 3px 0 0 3px; }

.book-eyebrow { font-size: 0.58rem; letter-spacing: 0.3em; text-transform: uppercase;
                font-weight: 500; color: #C42024;
                padding-bottom: 12px; border-bottom: 3px solid #17150F; }
.nb-page h2 { margin-top: 20px; font-family: 'Onest', system-ui, sans-serif;
              font-weight: 500; font-size: 2.1rem; line-height: 1.04;
              letter-spacing: -0.042em; color: #17150F; }
.book-intro { margin-top: 18px; max-width: 20rem;
              font-size: 0.86rem; line-height: 1.6; color: rgba(23, 21, 15, 0.82); }
.book-folio { position: absolute; left: 30px; right: 30px; bottom: 26px;
              padding-top: 10px; border-top: 1px solid rgba(23, 21, 15, 0.2);
              font-size: 0.54rem; letter-spacing: 0.3em; text-transform: uppercase;
              color: rgba(23, 21, 15, 0.4); }

.book-list { list-style: none; padding: 0; margin: 0; counter-reset: bl; }
.book-list li { position: relative; padding: 8px 0;
                border-top: 1px solid rgba(23, 21, 15, 0.22); }
/* a heavy rule to open the column, so it starts on something */
.book-list li:first-child { border-top: 3px solid #17150F; }
.book-list li:last-child { border-bottom: 1px solid rgba(23, 21, 15, 0.16); }
.book-list li::before {
  counter-increment: bl;
  content: counter(bl, decimal-leading-zero);
  font-family: 'Onest', system-ui, sans-serif;
  /* in the margin, not indenting the text — set inside the column it cost
     every description an extra line and the sixth item fell off the page */
  position: absolute; left: -26px; top: 10px;
  font-size: 0.58rem; letter-spacing: 0.04em; font-weight: 500; color: #C42024;
}
.book-list b { display: block; font-family: 'Onest', system-ui, sans-serif;
               font-weight: 500; font-size: 0.78rem; letter-spacing: -0.015em;
               color: #17150F; }
.book-list span { display: block; margin-top: 2px; font-size: 0.68rem; line-height: 1.44;
                  color: rgba(23, 21, 15, 0.82); }
.book-list a { color: #C42024; text-decoration: none;
               box-shadow: inset 0 -1px 0 rgba(196, 32, 36, 0.4); }

/* ---------- the sticky tabs ---------- */
.book-tabs { position: absolute; top: 34px; left: 100%; margin-left: -14px;
             z-index: -1; display: flex; flex-direction: column; gap: 8px; }
.book-tab {
  border: 0; cursor: pointer; font: inherit; font-size: 0.62rem;
  letter-spacing: 0.12em; font-weight: 500; white-space: nowrap;
  /* right-aligned with room on the left, because the left 14px of every tab
     is tucked behind the page and would otherwise eat the first letter */
  padding: 9px 12px 9px 26px; text-align: right;
  border-radius: 0 3px 3px 0;
  background: var(--hue); color: var(--tink);
  transform: translateX(0);
  transition: transform 0.25s cubic-bezier(0.2, 0.9, 0.3, 1), filter 0.2s ease;
  box-shadow: 2px 2px 6px rgba(30, 12, 10, 0.3);
}
.book-tab:hover { transform: translateX(8px); }
.book-tab.on { transform: translateX(14px); filter: saturate(1.1); }

.book-x { position: absolute; top: -36px; right: 0; width: 34px; height: 34px;
          border: 0; background: none; cursor: pointer; font-size: 1.5rem; line-height: 1;
          color: rgba(244, 240, 232, 0.5); font-family: inherit; }
.book-x:hover { color: #F4F0E8; }

@media (prefers-reduced-motion: reduce) {
  .card, .card-back, .apps-back, .apps-grid li,
  .book-back, .book-stage, .nb, .nb-cover, .nb-page > * { transition: none; }
}

/* ============================================================
   THE KEYBOARD — opened by the one on the desk

   Sixty-odd inert keys and one that works. The dead keys are what
   make it a keyboard; the red one is the only thing to press, so
   it is the only thing given any weight.
   ============================================================ */
.keys-wrap { position: fixed; inset: 0; z-index: 21; display: grid; place-items: center;
             padding: 24px; }
.keys-wrap[hidden] { display: none; }

.keys-back { position: absolute; inset: 0; background: rgba(10, 6, 6, 0.68);
             backdrop-filter: blur(5px); opacity: 0; transition: opacity 0.4s ease; }
.keys-wrap.open .keys-back { opacity: 1; }

.keys {
  position: relative; width: min(46rem, 100%);
  padding: clamp(22px, 3vw, 34px);
  background: #FBF9F5; color: #17150F;
  border-top: 7px solid #C42024;
  box-shadow: 0 30px 70px rgba(30, 12, 10, 0.5);
  opacity: 0; transform: translateY(34px) scale(0.94);
  transition: opacity 0.34s ease, transform 0.5s cubic-bezier(0.2, 0.9, 0.3, 1);
}
.keys-wrap.open .keys { opacity: 1; transform: none; }

.keys-eyebrow { font-size: 0.58rem; letter-spacing: 0.3em; text-transform: uppercase;
                font-weight: 500; color: #C42024;
                padding-bottom: 12px; border-bottom: 3px solid #17150F; }
.keys h2 { margin-top: 18px; font-family: 'Onest', system-ui, sans-serif;
           font-weight: 500; font-size: clamp(1.4rem, 3vw, 2rem); line-height: 1.06;
           letter-spacing: -0.042em; }

/* ---------- the board ----------
   Every row totals fifteen units and every key is flex-grown by its own
   count off a zero basis, so the rows align without any of them being
   measured or a width being written down.

   The board is laid back in perspective, and every key is extruded with a
   run of hard box-shadows at one-pixel steps — offset with no blur, they
   stack into a solid side wall. That costs nothing next to building six
   faces per key, and it survives being tilted. */
.keys { perspective: 1200px; perspective-origin: 50% 34%; }

.keys-board {
  margin-top: 24px; padding: 16px 16px 20px;
  border-radius: 10px;
  background: linear-gradient(180deg, #24211A, #131109);
  transform: rotateX(19deg);
  transform-origin: 50% 100%;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.1),
    inset 0 -3px 8px rgba(0, 0, 0, 0.6),
    0 3px 0 #0C0A06,
    0 6px 0 #090702,
    0 22px 40px rgba(10, 4, 4, 0.55);
}
.keys-row { display: flex; gap: 6px; }
.keys-row + .keys-row { margin-top: 7px; }

.keys-key {
  flex: var(--w, 1) 0 0;
  height: clamp(28px, 3.6vw, 40px);
  border: 0; border-radius: 5px 5px 7px 7px;
  background: linear-gradient(180deg, #423D33 0%, #322E26 42%, #272419 100%);
  color: rgba(251, 249, 245, 0.82);
  font: inherit; font-size: clamp(0.5rem, 0.9vw, 0.66rem); font-weight: 500;
  display: grid; place-items: center; cursor: default;
  transition: transform 0.08s ease, box-shadow 0.08s ease;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.16),
    inset 0 -2px 3px rgba(0, 0, 0, 0.5),
    0 1px 0 #1D1A14, 0 2px 0 #1A1711, 0 3px 0 #17140E,
    0 4px 0 #14110B, 0 5px 0 #110E08,
    0 7px 7px rgba(0, 0, 0, 0.5);
}
.keys-key.is-space { background: linear-gradient(180deg, #3A362D, #232019); }

/* the one that works: the same extrusion in red, and it goes down when hit */
.keys-key.is-go {
  background: linear-gradient(180deg, #E03036 0%, #C42024 45%, #A5171B 100%);
  color: #FFF6F2; cursor: pointer; letter-spacing: 0.06em;
  animation: keyPulse 2.1s ease-in-out infinite;
}
.keys-key.is-go:hover {
  background: linear-gradient(180deg, #F03A40, #CE2126 45%, #AC191D 100%);
}
.keys-key.is-go:active {
  transform: translateY(4px);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.2),
    0 1px 0 #6E0E11, 0 2px 4px rgba(0, 0, 0, 0.5);
}
@keyframes keyPulse {
  0%, 100% {
    box-shadow:
      inset 0 1px 0 rgba(255, 255, 255, 0.22),
      inset 0 -2px 3px rgba(0, 0, 0, 0.35),
      0 1px 0 #96151A, 0 2px 0 #8B1317, 0 3px 0 #7E1013,
      0 4px 0 #720E11, 0 5px 0 #660C0F,
      0 7px 7px rgba(0, 0, 0, 0.5), 0 0 0 0 rgba(224, 48, 54, 0.55);
  }
  55% {
    box-shadow:
      inset 0 1px 0 rgba(255, 255, 255, 0.22),
      inset 0 -2px 3px rgba(0, 0, 0, 0.35),
      0 1px 0 #96151A, 0 2px 0 #8B1317, 0 3px 0 #7E1013,
      0 4px 0 #720E11, 0 5px 0 #660C0F,
      0 7px 7px rgba(0, 0, 0, 0.5), 0 0 0 12px rgba(224, 48, 54, 0);
  }
}

.keys-foot { margin-top: 18px; padding-top: 13px; font-size: 0.62rem;
             letter-spacing: 0.14em; text-transform: uppercase;
             border-top: 3px solid #17150F; color: rgba(23, 21, 15, 0.6); }

.keys-x { position: absolute; top: 12px; right: 14px; width: 34px; height: 34px;
          border: 0; background: none; cursor: pointer; font-size: 1.5rem; line-height: 1;
          color: rgba(23, 21, 15, 0.65); font-family: inherit; }
.keys-x:hover { color: #C42024; }

@media (prefers-reduced-motion: reduce) {
  .keys, .keys-back, .keys-key.is-go { transition: none; animation: none; }
}
