/* ==========================================================================
   HOME BOOK — OHMSTRIKE / Jérôme Marcucci
   Implemented from 00_SPEC/HOME_BOOK_SPEC.md + DESIGN_TOKENS.json.
   Documented deviations from the locked spec (approved, see 04_QA/QA_REPORT.md):
     - grid opacities are the spec values x0.22 (match the layout masters);
     - button border gradient runs 328deg (masters show cyan at the left edge).
   ========================================================================== */

/* --------------------------------------------------------------- reset --- */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html { background: #0b1511; }
body {
  min-height: 100vh;
  min-height: 100dvh;
  overflow: hidden;
  background: #0b1511;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
img { display: block; max-width: 100%; }
p { margin: 0; }

/* --------------------------------------------------------------- fonts --- */
@font-face {
  font-family: "Inter";
  src: url("/assets/fonts/inter-variable.woff2") format("woff2");
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
}
@font-face {
  font-family: "Instrument Sans";
  src: url("/assets/fonts/instrument-sans-variable.woff2") format("woff2");
  font-style: normal;
  font-weight: 400 700;
  font-stretch: 75% 100%;
  font-display: swap;
}

/* ------------------------------------------------------- shared tokens --- */
:root {
  --grid-rgb: 193 193 193;

  /* defaults for T01-T08 and T10; theme overrides sit in the theme block */
  --rule-lockup: #075f5f;
  --rule-pdf: rgb(21 182 182 / .40);
  --pdf-color: #a8d5d5;
  --name-color: #ffffff;

  --cta-bg: #ffffff;
  --cta-lead: #484848;
  --cta-border: linear-gradient(328deg, #064949 0%, #14f5f5 100%);
  --cta-accent: linear-gradient(180deg, #854c30 0%, #d85110 100%);
  --cta-halo: 20 245 245;
  --cta-focus: #14f5f5;

  /* Grain: static fractal noise, flattened to the very low contrast measured
     inside the layout masters (high-pass sigma ~0.5-1.5 grey levels). */
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='g' x='0' y='0' width='100%25' height='100%25' color-interpolation-filters='sRGB'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='1' stitchTiles='stitch' seed='11'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncR type='linear' slope='0.5' intercept='-0.2'/%3E%3CfeFuncG type='linear' slope='0.5' intercept='-0.2'/%3E%3CfeFuncB type='linear' slope='0.5' intercept='-0.2'/%3E%3CfeFuncA type='linear' slope='0' intercept='1'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23g)'/%3E%3C/svg%3E");
}

/* ------------------------------------------------------ theme variables ---
   grid-a  = spec overall opacity x 0.22 (approved master-matched scale)
   blk-a   = black base opacity        blk-2 = secondary layer @33%
   blk-b   = outer border @33%         grain-a = grain layer opacity          */
[data-theme="01"] { --grid-a: .0660; --blk-a: .68; --blk-2:  78 151 108; --blk-b: 139 254 168; --grain-a: .22; }
[data-theme="02"] { --grid-a: .0297; --blk-a: .58; --blk-2:  82 108 108; --blk-b:  29 163 144; --grain-a: .22; }
[data-theme="03"] { --grid-a: .0363; --blk-a: .58; --blk-2:  82 108 108; --blk-b:  29 163 144; --grain-a: .22; }
[data-theme="04"] { --grid-a: .0297; --blk-a: .58; --blk-2:  82 108 108; --blk-b:  29 163 144; --grain-a: .22; }
[data-theme="05"] { --grid-a: .0363; --blk-a: .58; --blk-2:  82 108 108; --blk-b:  29 163 144; --grain-a: .22; }
[data-theme="06"] { --grid-a: .0363; --blk-a: .18; --blk-2:  44  87  68; --blk-b:  29 163 144; --grain-a: .48; }
[data-theme="07"] { --grid-a: .0297; --blk-a: .43; --blk-2:  55  68  75; --blk-b: 134 186 215; --grain-a: .51; }
[data-theme="08"] { --grid-a: .0297; --blk-a: .43; --blk-2:  55  68  75; --blk-b: 134 186 215; --grain-a: .51; }
[data-theme="09"] { --grid-a: .0297; --blk-a: .43; --blk-2:  37  29  26; --blk-b: 199 188 168; --grain-a: .51; }
[data-theme="10"] { --grid-a: .0363; --blk-a: .43; --blk-2:   9  15  63; --blk-b: 110 205 146; --grain-a: .51; }

/* T09 theme overrides — line 06, button contour, PDF rule and the halo/focus
   accent that must stay coherent with that contour. Desktop and mobile alike. */
[data-theme="09"] {
  --rule-lockup: #5a554c;
  --rule-pdf: rgb(199 188 168 / .40);
  --pdf-color: #9f9f9e;
  --cta-border: linear-gradient(328deg, #5b5349 0%, #dad2bb 100%);
  --cta-halo: 218 210 187;
  --cta-focus: #dad2bb;
}

/* =========================================================================
   DESKTOP COMPOSITION — reference viewport 1920 x 1080 CSS px
   Locked stack: 104 top / 132 monogram / 104 gap / 328 block / 412 bottom.
   Free vertical space is distributed 104:412, so 1920x1080 is exact and
   other heights stay proportional.
   ========================================================================= */
:root {
  --vh: 100vh;
  --vh: 100dvh;
  /* One desktop design pixel. It is exactly 1px at every normal viewport and
     shrinks only when the height can no longer hold the locked 564px stack
     (small landscape windows), so the composition scales instead of clipping. */
  --k: min(1px, calc(var(--vh) / 720));
  --free: max(0px, calc(var(--vh) - 564 * var(--k)));
  --mono-top: calc(var(--free) * 0.2015504);
  --mono-h: calc(132 * var(--k));

  --block-top: calc(var(--mono-top) + 236 * var(--k));
  --block-left: calc(50vw - 234 * var(--k));
  --block-w: calc(468 * var(--k));
  --block-h: calc(328 * var(--k));
  --block-r: calc(7 * var(--k));
  --block-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 468 328' preserveAspectRatio='none'%3E%3Crect width='468' height='328' rx='7' fill='%23fff'/%3E%3C/svg%3E");

  --grid-x: 15px;
  --grid-y: 21px;

  /* Element offsets measured from the block top. The revised desktop masters
     place the block at the locked y=340 and everything inside it 2 px higher
     than the previous masters did, which resolved the earlier 2 px ambiguity
     between the locked block position and the reference image. */
  --lockup-w: calc(234 * var(--k));
  --wm-top: calc(42 * var(--k));
  --rule06-top: calc(64 * var(--k));
  --name-top: calc(74 * var(--k));
  --name-fs: calc(22.5 * var(--k));
  --cta-top: calc(141 * var(--k));
  --cta-w: calc(306 * var(--k));
  --cta-h: calc(45 * var(--k));
  --cta-r: calc(7 * var(--k));
  --cta-fs: calc(18 * var(--k));
  --rule09-top: calc(242 * var(--k));
  --rule09-w: calc(54 * var(--k));
  --pdf-top: calc(252.7 * var(--k));
  --pdf-fs: calc(15 * var(--k));
}

/* ---------------------------------------------------------- background --- */
.bg { position: fixed; inset: 0; z-index: 0; }
.bg picture { display: block; }
.bg img {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* --------------------------------------------------- 02 decorative grid ---
   CSS only. Vertical cycle 18px = 1 major + 2 minors at 30%; horizontal 25px.
   Masked out of the central block; the background stays visible through it.  */
.grid {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background-image:
    repeating-linear-gradient(to right,
      rgb(var(--grid-rgb) / var(--grid-a)) 0 1px,
      transparent 1px 6px,
      rgb(var(--grid-rgb) / calc(var(--grid-a) * .3)) 6px 7px,
      transparent 7px 12px,
      rgb(var(--grid-rgb) / calc(var(--grid-a) * .3)) 12px 13px,
      transparent 13px 18px),
    repeating-linear-gradient(to bottom,
      rgb(var(--grid-rgb) / var(--grid-a)) 0 1px,
      transparent 1px 25px);
  background-position: var(--grid-x) 0, 0 var(--grid-y);
  -webkit-mask-image: linear-gradient(#000, #000), var(--block-mask);
          mask-image: linear-gradient(#000, #000), var(--block-mask);
  -webkit-mask-position: 0 0, var(--block-left) var(--block-top);
          mask-position: 0 0, var(--block-left) var(--block-top);
  -webkit-mask-size: 100% 100%, var(--block-w) var(--block-h);
          mask-size: 100% 100%, var(--block-w) var(--block-h);
  -webkit-mask-repeat: no-repeat, no-repeat;
          mask-repeat: no-repeat, no-repeat;
  -webkit-mask-composite: xor;
          mask-composite: exclude;
}
@supports not ((mask-composite: exclude) or (-webkit-mask-composite: xor)) {
  .grid { display: none; }
}

/* --------------------------------------------------------------- stage --- */
.stage { position: fixed; inset: 0; z-index: 2; }

/* ------------------------------------------------------- 03 monogram --- */
.monogram {
  position: absolute;
  top: var(--mono-top);
  left: 50%;
  transform: translateX(-50%);
  height: var(--mono-h);
  width: auto;
  aspect-ratio: 21.57 / 73.49;
}

/* --------------------------------------------------- 04 central block --- */
.block {
  position: absolute;
  top: var(--block-top);
  left: var(--block-left);
  width: var(--block-w);
  height: var(--block-h);
  border-radius: var(--block-r);
  background-image:
    linear-gradient(rgb(var(--blk-2) / .33), rgb(var(--blk-2) / .33)),
    linear-gradient(rgb(0 0 0 / var(--blk-a)), rgb(0 0 0 / var(--blk-a)));
  overflow: hidden;
}
/* 1 CSS px outer border, drawn inside the box so every element offset below
   is measured from the block edge itself. */
.block::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  border: 1px solid rgb(var(--blk-b) / .33);
  border-radius: inherit;
  pointer-events: none;
}
/* Grain (spec 8): static, clipped to the block, inherits its radius, never
   interactive. It lives in the root stacking context so that color-dodge
   blends against the real composite of block + background, exactly as the
   Photoshop layer does — inside .stage it would be isolated from the
   background image and would darken instead of lift. */
.grain {
  position: fixed;
  z-index: 3;
  top: var(--block-top);
  left: var(--block-left);
  width: var(--block-w);
  height: var(--block-h);
  border-radius: var(--block-r);
  overflow: hidden;
  pointer-events: none;
  background-image: var(--grain);
  background-repeat: repeat;
  mix-blend-mode: color-dodge;
  opacity: var(--grain-a);
}

/* ------------------------------------------- 05 + 06 + 07 title lockup ---
   Wordmark, rule and name share one visible width and one centre line.      */
.lockup { display: contents; }

.wordmark {
  position: absolute;
  top: var(--wm-top);
  left: 50%;
  transform: translateX(-50%);
  width: var(--lockup-w);
  height: auto;
  aspect-ratio: 233.91 / 14.04;
}

.rule { position: absolute; left: 50%; transform: translateX(-50%); height: 1px; }
.rule--lockup { top: var(--rule06-top); width: var(--lockup-w); background: var(--rule-lockup); }
.rule--pdf    { top: var(--rule09-top); width: var(--rule09-w); background: var(--rule-pdf); }

.name {
  position: absolute;
  top: var(--name-top);
  left: 0;
  width: 100%;
  text-align: center;
  line-height: 1;
  color: var(--name-color);
  font-family: "tt-commons-pro", "Instrument Sans", "Inter", Arial, sans-serif;
  font-weight: 500;
  font-size: var(--name-fs);
  letter-spacing: .05em;
}
.name__ink {
  display: inline-block;
  white-space: nowrap;
  margin-right: -.05em; /* neutralise trailing tracking so the ink stays centred */
}

/* -------------------------------------------------------- 08 button --- */
.cta {
  position: absolute;
  top: var(--cta-top);
  left: 50%;
  margin-left: calc(var(--cta-w) / -2);
  width: var(--cta-w);
  height: var(--cta-h);
  border-radius: var(--cta-r);
  display: flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  white-space: nowrap;
  font-family: "Inter", Arial, sans-serif;
  font-weight: 400;
  font-size: var(--cta-fs);
  line-height: 1;
  letter-spacing: .14em;
  color: var(--cta-lead);
}
/* The capsule lives on its own layer so it can breathe and grow on hover while
   the text layer below is never transformed — no glyph re-rasterisation, no
   wobble, no reflow. */
.cta__shell {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  border: 1px solid transparent;
  background:
    linear-gradient(var(--cta-bg), var(--cta-bg)) padding-box,
    var(--cta-border) border-box;
  animation: cta-breathe 8s cubic-bezier(.37, 0, .63, 1) infinite;
  transition: scale .45s cubic-bezier(.22, .61, .36, 1), box-shadow .45s ease;
  pointer-events: none;
  will-change: transform;
}
/* text layer: stable, always on top, never scaled */
.cta__label { white-space: pre; position: relative; z-index: 1; }
.cta__lead { color: var(--cta-lead); }
.cta__accent {
  background-image: var(--cta-accent);
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
  margin-right: -.14em; /* neutralise trailing tracking */
}
/* Halo (spec 10). Two independent layers outside the button box, so the button
   and its label never scale, move, rotate or change border thickness:
     ::before — the continuous 8 s breathing halo;
     ::after  — a static accent that reinforces hover and activation.
   Keeping them separate means pausing the breathing on hover can never
   suppress the hover state, and resuming it never jumps.                      */
/* Halos hang off the shell, so they follow the capsule as it breathes and grows:
     ::before — the continuous 8 s breathing halo;
     ::after  — the hover / activation accent.                                 */
.cta__shell::before,
.cta__shell::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
}
.cta__shell::before {
  animation: cta-halo 8s cubic-bezier(.37, 0, .63, 1) infinite;
  /* Idle only: the shadow geometry is now fixed. Interpolating blur/spread
     forced a repaint every frame; the halo instead takes its change of extent
     from the shell's scale and animates opacity alone, so the whole breath
     stays on the compositor. The hover state below is untouched. */
  box-shadow: 0 0 22px 3px rgb(var(--cta-halo) / .6);
  opacity: .242;
  will-change: opacity;
}
.cta__shell::after {
  box-shadow: 0 0 26px 4px rgb(var(--cta-halo) / .55);
  opacity: 0;
  transition: opacity .45s ease;
}
.cta:focus-visible {
  outline: 2px solid var(--cta-focus);
  outline-offset: 3px;
}
/* Activation: a restrained static accent only — no displacement. */
.cta:active .cta__shell::after { opacity: .6; transition-duration: .12s; }

/* ------------------------------------------------- 10 PDF information --- */
.pdf {
  position: absolute;
  top: var(--pdf-top);
  left: 0;
  width: 100%;
  text-align: center;
  line-height: 1;
  color: var(--pdf-color);
  font-family: "Instrument Sans", "Inter", Arial, sans-serif;
  font-weight: 400;
  font-stretch: 100%;
  font-size: var(--pdf-fs);
  letter-spacing: .012em;
}

/* --------------------------------------------------- hover-only accent ---
   Hover behaviour is limited to devices with a genuine hover-capable fine
   pointer, and is never synthesised on touch. The button geometry and its
   typography stay identical in every state.                                  */
@media (hover: hover) and (pointer: fine) {
  /* Hover reads clearly stronger than the idle breath: the capsule grows via
     the standalone `scale` property, which composes with the breathing
     `transform` instead of fighting it — so entering and leaving hover stay
     continuous and the breath never has to be frozen or restarted. */
  .cta:hover .cta__shell {
    scale: 1.035;
    box-shadow:
      0 0 0 1px rgb(var(--cta-halo) / .5),
      0 0 22px 2px rgb(var(--cta-halo) / .3);
  }
  .cta:hover .cta__shell::after { opacity: .8; }
}

/* Exactly 8 s, shared by both breathing layers so scale and halo stay in phase.
   Only the shell moves; the label layer is never transformed. */
/* Start and end share one declaration, so the loop closes on an identical value
   and cannot snap. The near-sinusoidal easing keeps velocity continuous through
   the turning point at 50% and across the boundary at 0/100%. */
@keyframes cta-breathe {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.022); }
}
/* Idle halo presence lifted by 10% relative at both ends (.22->.242,
   .66->.726). Duration, easing, phase, ratio and shadow geometry unchanged. */
@keyframes cta-halo {
  0%, 100% { opacity: .242; }
  50%      { opacity: .726; }
}

/* =========================================================================
   MOBILE COMPOSITION — 360 x 780 CSS px scene, uniformly scaled and centred.
   --u is one design pixel; every geometry value is a multiple of it.
   ========================================================================= */
@media (max-width: 767px) {
  :root {
    --u: min(calc(100vw / 360), calc(100vh / 780));
    --u: min(calc(100vw / 360), calc(100svh / 780));
    --u: min(calc(100vw / 360), calc(100dvh / 780));

    --scene-x: calc(50vw - 180 * var(--u));
    --scene-y: calc(50 * var(--vh) / 100 - 390 * var(--u));

    --mono-top: calc(var(--scene-y) + 67.5 * var(--u));
    --mono-h: calc(95.5 * var(--u));

    --block-top: calc(var(--scene-y) + 255 * var(--u));
    --block-left: calc(var(--scene-x) + 11.25 * var(--u));
    --block-w: calc(337.5 * var(--u));
    --block-h: calc(271.5 * var(--u));
    --block-r: calc(14 * var(--u));
    --block-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 337.5 271.5' preserveAspectRatio='none'%3E%3Crect width='337.5' height='271.5' rx='14' fill='%23fff'/%3E%3C/svg%3E");

    --lockup-w: calc(208.5 * var(--u));
    --wm-top: calc(17 * var(--u));
    --rule06-top: calc(37 * var(--u));
    --name-top: calc(44.05 * var(--u));
    --name-fs: calc(21.1 * var(--u));
    --cta-top: calc(113 * var(--u));
    --cta-w: calc(280 * var(--u));
    --cta-h: calc(42.5 * var(--u));
    --cta-r: calc(7.5 * var(--u));
    --cta-fs: calc(18 * var(--u));
    --rule09-top: calc(230 * var(--u));
    --rule09-w: calc(49.5 * var(--u));
    --pdf-top: calc(239.7 * var(--u));
    --pdf-fs: calc(14.5 * var(--u));

    /* Intentional art-direction reversal of the accent gradient on mobile. */
    --cta-accent: linear-gradient(180deg, #d85110 0%, #854c30 100%);
  }
  /* The 8 s halo breathing continues here; only hover-dependent decoration is
     withheld, and that is already gated by the hover media query above. */
}

/* ------------------------------------------------- motion preferences --- */
@media (prefers-reduced-motion: reduce) {
  /* No continuous animation and no motion on hover, but the control still
     reads clearly as interactive: the halo and the outer ring remain. */
  .cta__shell { animation: none; transition: none; }
  .cta__shell::before { animation: none; opacity: .32; }
  .cta__shell::after { transition: none; }
  .cta:hover .cta__shell { scale: none; }
}
/* QA capture mode (spec 17.3): freeze the capsule and halo so comparison
   captures against the layout masters are deterministic. */
[data-anim="0"] .cta__shell { animation: none; transition: none; scale: none; box-shadow: none; }
[data-anim="0"] .cta__shell::before { animation: none; opacity: 0; }
[data-anim="0"] .cta__shell::after { transition: none; opacity: 0; }

/* =========================================================================
   BLOCK INSPECTION MODE — hidden QA / art-direction view, activated only by
   ?inspect=block. It strips the environment (background image, grid, grain)
   and every decorative button effect so the central block and its internal
   spacing can be assessed from a deterministic screenshot.

   It changes nothing that occupies layout: the removed layers are all fixed
   or absolutely positioned, and the button effects are transform/shadow only,
   so geometry is identical to the normal composition. The missing grain is
   intentional and is deliberately NOT compensated for elsewhere.
   ========================================================================= */
[data-inspect="block"],
[data-inspect="block"] body { background: #101010; }

[data-inspect="block"] .bg,
[data-inspect="block"] .grid,
[data-inspect="block"] .grain { display: none; }

[data-inspect="block"] .cta__shell {
  animation: none;
  transition: none;
  scale: none;
  box-shadow: none;
}
[data-inspect="block"] .cta__shell::before,
[data-inspect="block"] .cta__shell::after { display: none; }
[data-inspect="block"] .cta:hover .cta__shell,
[data-inspect="block"] .cta:active .cta__shell { scale: none; box-shadow: none; }
