/**
 * desk2026.css — the "FLUINT web 2026" redesign.
 *
 * Lives apart from the live site on purpose: this is the new Figma
 * (3dyXuCol40KhCuJ12DyevU) and it shares nothing with the Tailwind build in
 * `build.css`, which cannot be recompiled here (see README). Plain CSS, no
 * build step, per CLAUDE.md.
 *
 * Geometry: every desktop frame is a fixed 1520x920 canvas of absolutely
 * placed nodes, so that is what this reproduces. `--k` is one design pixel —
 * 1px at 1520 and proportionally smaller below — which scales the canvas as a
 * unit instead of re-guessing each value in rem. Every offset below is the
 * Figma x/y of that node; see `docs/figma-2026-map.md`.
 */

:root {
  --k: calc(min(100vw, 1520px) / 1520);
  --fl-track: 0.04em;

  /* ── full width, above 1520 ──────────────────────────────────────
     The file draws 1520. Above that, the canvas centres and leaves air at
     the sides: `--fl-bleed` is that air, measured per side, and 0 below
     1520. The photos that fill the whole screen in the frame eat it up.

     The Play Time model (play-time.es, the reference Valerie gave on
     6-ago-2026; measured in docs/edge-to-edge-2026.md): the photo FILLS the
     window and crops off what is left over. It does not grow in height: it
     keeps the height the file gives it and the crop takes its toll on the
     sides. Which is why there are no per-block caps here — the photo follows
     the window as far as it has to, and what is paid above the asset's
     resolution is rescaling, not air.

     `--fl-gutter` is the real air, which `effects.js` measures off the
     canvas itself (the fallback covers the time before it runs). It is
     reused instead of recalculated: `100vw` includes the scrollbar and
     would give too much. */
  --fl-bleed: var(--fl-gutter, max(0px, (100vw - 1520px) / 2));

  /* The lens radius of the "bubble" effect. NOT IN THE SOURCE: the Guide says
     which elements bubble and how the effect is made, never how big the disc
     is. 110 design px is roughly two or three glyphs at heading size, and it
     is an invention until a spec says otherwise. */
  --fl-lens: 110;
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
  background: #fff;
  overflow-x: hidden;
}

/* ── the design canvas ─────────────────────────────────────────────
   1520 wide, centred. The home frame is 920 tall in Figma and the first
   screen is exactly the window, taller or shorter: the hero photo — and the
   panorama over it — fill whatever height they are given, and the two
   anchored blocks (nav at the top, title + CTA at the bottom) hold their
   distance to the edge they were designed against. */

.fl-frame {
  position: relative;
  width: 100%;
  max-width: 1520px;
  /* Exactly the window, not a pixel more. The frame is 920 tall and that
     number was the FLOOR — `min-height: 100svh` only stretched it upward —,
     so in a window shorter than 920 the first screen stuck out below the
     edge and took the CTA with it: +160 px at 1536x760, +175 at
     1280x600, measured (Denis, 24-ago-2026).
     The height never depended on the width: the photo fills and crops, and
     the panorama occupies its same box, so it can simply be the window's.
     `svh` and not `dvh` so it also fits with the mobile bar expanded and does
     not jump when it hides — it is what the mobile block already did.
     At 1520x920, which is where parity with the frame is measured, it comes
     out to the same 920. */
  height: 100svh;
  margin: 0 auto;
  /* The photo bleeds out to the sides, so there can be no clipping here. No
     risk: the only children are the photo, the title and the CTA, and the
     last two live inside the canvas. Whatever sticks out at the sides is cut
     by body's `overflow-x: hidden`.
     (`overflow: clip` + `overflow-clip-margin` was tried: the margin computes
     to 0px and the bleed existed as a box but was not painted.) */
  overflow: visible;
}

.fl-frame > * {
  position: absolute;
  margin: 0;
}

/* The title and the CTA are NOT touched: they stay where the file puts them,
   on the 1520 canvas. Only the photo bleeds out. */
.fl-hero-img {
  left: calc(-1 * var(--fl-bleed));
  top: 0;
  /* Explicit width and not `auto`: an absolute <img> with `width:auto`
     resolves its intrinsic width against the height and ignores left/right,
     and a 1652 hero came out in a 1520 window. */
  width: calc(100% + 2 * var(--fl-bleed));
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* The panorama sits in the photo's own box — same bleed, same height — so the
   two are interchangeable and every measurement of the hero keeps holding.
   `pointer-events: none` is not decoration: Marzipano's controls are disabled
   in JS, but the viewer's own container would still eat the wheel, and with it
   the page scroll over the whole first screen.
   It starts transparent and is revealed by `hero360.js` on the first complete
   frame, so what the visitor sees while the room loads is the render. */
.fl-hero-pano {
  left: calc(-1 * var(--fl-bleed));
  top: 0;
  width: calc(100% + 2 * var(--fl-bleed));
  height: 100%;
  pointer-events: none;
  opacity: 0;
  transition: opacity 600ms ease;
}

.fl-hero-pano.is-ready {
  opacity: 1;
}

/* Every text node in the file carries `lineHeightPx = 1.2 x fontSize` — read
   from the raw API, which reports the line height the simplified tree omits.
   The 0.09em padding this used to carry was an approximation of the same
   half-leading against a line box of 1, and it double-counted the moment a
   node was given its real 1.2. */
.fl-display {
  font-family: 'Gloock', Georgia, serif;
  font-weight: 400;
  letter-spacing: var(--fl-track);
  line-height: 1.2;
  white-space: nowrap;
}

.fl-text {
  font-family: 'Lato', Arial, sans-serif;
  font-weight: 400;
  line-height: 1.2;
  white-space: nowrap;
}

/* ── nav (#2074:53) — 68,16 · 1387x48 ─────────────────────────────
   The link offsets are hand-placed in the design, not evenly spaced, and a
   flex gap would drift the moment a language changes a word's width. Same
   offsets as Figma, relative to the nav box. */

/* The hero's own `.fl-nav` (#2074:53) left with the travelling bar of
   2026-08-05: the corpus's `.fl-menu` is the only bar. Its rules went in the
   final audit of 3-sep-2026. */

/* logo (#3:9) — 625,0 inside the nav · 138x48. The `f` and `luint` overlap by
   16px in the design; they are two nodes, not one word. */
.fl-logo {
  position: absolute;
  left: calc(625 * var(--k));
  top: 0;
  width: calc(138 * var(--k));
  height: calc(48 * var(--k));
  color: #fff;
  text-decoration: none;
}

/* `.fl-warp` is in every one of these because the logo is the only lens target
   whose copy carries elements of its own, and they are placed by rules that
   need `.fl-logo` as an ancestor — which the copy, living in the lens layer,
   does not have. Without them the two nodes fall back to inline flow at the
   inherited size and the bubble shows a second, smaller `fluint` across the
   first (Denis, 2026-08-05). `effects.js` copies the type of the element it
   doubles, never of its children. */
.fl-logo span,
.fl-warp .fl-logo-f,
.fl-warp .fl-logo-rest {
  position: absolute;
  top: 0;
  font-size: calc(48 * var(--k));
}

.fl-logo .fl-logo-f,
.fl-warp .fl-logo-f {
  left: 0;
  letter-spacing: normal;
}

.fl-logo .fl-logo-rest,
.fl-warp .fl-logo-rest {
  left: calc(24 * var(--k));
}

/* ── hero copy ─────────────────────────────────────────────────────
   Title #5:23 at 68,770 and CTA group #2074:51 at 608,801, both anchored to
   the bottom of the frame (150 and 119 design px up from 920) so a taller
   viewport grows the photo, not the gap under the text. */

/* The box is given the design's own height (107) so that anchoring it 43 up
   from the frame's bottom lands its top on the design's 770, whatever
   line-height Gloock resolves to. */
.fl-hero-title {
  left: calc(68 * var(--k));
  bottom: calc(43 * var(--k));
  height: calc(107 * var(--k));
  font-size: calc(96 * var(--k));
  color: #fff;
}

.fl-hero-cta {
  left: calc(608 * var(--k));
  bottom: calc(52 * var(--k));
  width: calc(499 * var(--k));
  height: calc(67 * var(--k));
  color: #fff;
  text-decoration: none;
}

.fl-hero-cta span {
  --fl-lens: 41;

  position: absolute;
  left: 0;
  top: calc(22 * var(--k));
  font-size: calc(18 * var(--k));
  font-style: italic;
  letter-spacing: var(--fl-track);
}

/* The 67px ring drawn over "360º" (#2003:22) is not built: it is where the
   design shows the cursor sitting, the same annotation the `_Mouse` component
   is elsewhere in the file, and not a piece of the interface (Denis,
   2026-08-05). Its own SVG is what the noise constants in `effects.js` were
   read from — that stays. The CTA keeps the bubble, at a disc sized for its
   own type: `--fl-lens` is declared in glyphs of the text it opens over, and
   110 against a 48px heading is the ratio the rest of the page uses. At 18px
   that is 41 — the fixed 110 swallowed the whole line and read as the text
   fading out rather than as a lens. */

/* ── phone — MBL_home (Penpot 660f3b08 file, board 9df34c28) ────────
   Measured against the 375x812 mobile frames of the Penpot file — see
   docs/mobile-penpot-spec.md. The bar over the hero is `.fl-menu`
   (corpus2026.css): one fixed bar for the whole page, logo + burger.
   The hero's own `.fl-nav` would double it, so it does not draw. */

@media (max-width: 799px) {
  :root {
    /* PURE scale, no cap: the 375 canvas grows with the window up to
       799, just as the 1520 one does on desktop. It used to be capped at
       480 and from there to 799 the boxes changed proportion and the photos
       re-framed; Denis settled it on 02-sep-2026: "las imágenes se
       escalan, no se mueven — el encuadre no cambia, crecen o decrecen".
       `--fl-vw` is written by effects.js in its measure(), with the REAL
       width (clientWidth): in a
       narrowed desktop browser, 100vw counts the scrollbar
       and the heights in --k came out ~4% long against the boxes of width
       100%% — 260 px of white after the open gallery (Denis, 02-sep).
       Same reason the large canvas uses --fl-gutter. */
    --k: calc(var(--fl-vw, 100vw) / 375);
  }

  /* Title 20,684 (48px, 58 tall) and CTA 20,639 (18 italic, 22 tall), both
     anchored to the frame's bottom edge: 812-684-58 = 70 and 812-639-22 = 151.
     A taller window grows the photo, not the gap under the text — same rule
     as the desktop frame. */
  .fl-hero-title {
    left: calc(20 * var(--k));
    bottom: calc(70 * var(--k));
    height: calc(58 * var(--k));
    font-size: calc(48 * var(--k));
  }

  /* `bottom` and `height` are set by the touch block in corpus2026.css (the
     board's 151 minus the 11 of finger room); here only what does not clash. */
  .fl-hero-cta {
    left: calc(20 * var(--k));
    width: calc(327 * var(--k));
  }

  .fl-hero-cta span {
    top: 0;
    font-size: calc(18 * var(--k));
  }
}


/* ── the "bubble" lens ─────────────────────────────────────────────
   > Efecto "bubble" al pasar el ratón sobre títulos, subtítulos y entradas. A
   > excepción de párrafos o textos con otras animaciones. En Figma he logrado
   > este efecto con el efecto "textura".            — Guide #2074:58

   A disc that follows the cursor, not the whole string (Denis, 2026-08-05).
   Two halves of the same disc: a filtered copy of the text masked to show only
   inside it, and the real text masked to give up exactly that disc. Overlaying
   the copy without cutting the hole leaves the clean letters showing through
   the displaced ones and reads as a smudge.

   The copy lives in its own layer over the canvas, never inside the element it
   doubles: nesting it would put it under the very mask that opens the hole,
   and inside an <a> or a <button> it would be a copy of an interactive
   element. `effects.js` places and types each one. */

.fl-defs {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

/* Whatever holds both frames — `effects.js` puts the layer there so it spans
   the page and not just the corpus. */
.fl-lens-host {
  position: relative;
}

.fl-lens-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
}

.fl-warp {
  position: absolute;
  margin: 0;
  padding: 0;
  opacity: 0;
  filter: url('#fl-bubble');
  transition: opacity 0.12s ease-out;
  mask-image: radial-gradient(
    circle calc(var(--fl-lens) * var(--k)) at var(--fl-x, -999px) var(--fl-y, -999px),
    #000 45%,
    transparent 100%
  );
  -webkit-mask-image: radial-gradient(
    circle calc(var(--fl-lens) * var(--k)) at var(--fl-x, -999px) var(--fl-y, -999px),
    #000 45%,
    transparent 100%
  );
}

.fl-warp.is-lit {
  opacity: 1;
}

/* A mask does not only make a hole: it also CLIPS, to the border box. So the
   moment this class lands, everything its element paints outside its own box
   loses its canvas — the disc never touches it, the clip erases it. The page
   has two of those and neither is text: `.fl-notebook-link` draws an arrow
   32px to the LEFT of its box (`corpus2026.css:746`) and `.fl-faq-link` one
   past its right edge (`corpus2026.css:1411`), both as pseudo-elements, and
   both went from 39 and 25 pixels of ink to a spotless 255 the instant the
   cursor arrived. Denis, 31-ago-2026: "cuando pones el raton encima no debe
   de desaparecer la flecha".

   Which is why the two of them hand the lens their SPAN and not themselves
   (`effects.js`): the span is the text, the text is what the bubble is for,
   and an arrow hanging off the link is simply not inside the thing being
   masked. It is also the only version of this that Chromium repaints
   correctly — `mask-clip: no-clip` does keep the arrow, and then leaves the
   pixels outside the border box holding the PREVIOUS mask until something
   else forces a repaint: measured on 2026-08-31, the arrow carried the shadow
   of a bubble the cursor had left 200px ago, and only a scroll cleared it.
   `tools/flecha-hover.js` measures the arrows. */
.fl-has-lens {
  mask-image: radial-gradient(
    circle calc(var(--fl-lens) * var(--k)) at var(--fl-x, -999px) var(--fl-y, -999px),
    transparent 45%,
    #000 100%
  );
  -webkit-mask-image: radial-gradient(
    circle calc(var(--fl-lens) * var(--k)) at var(--fl-x, -999px) var(--fl-y, -999px),
    transparent 45%,
    #000 100%
  );
}

/* The lens is handed these two spans, so each of them has to BE a box. An
   inline that wraps is two fragments and a mask over two fragments is painted
   as if they were one long unbroken line, while the copy is a single block
   over their union: the hole opens where the warped letters are not. Measured
   on 2026-08-31 — pointing at the second line of the quote warped BOTH lines
   at once and left the crisp letters showing through the displaced ones, which
   is the smudge `.fl-warp` exists to avoid. As a block the box is the link's:
   one child, same width, same two lines. */
.fl-notebook-link span,
.fl-faq-link span {
  display: block;
}

/* The disc is two or three glyphs of the text it opens over, and 110 is that
   against the 48px headings the figure was read from. On 16px type it covers
   the whole line, so what shows is the text fading out and no bubble at all —
   which is what Denis reported on the FAQ on 2026-08-05. Declared wherever the
   type is smaller, in the same proportion: 110 x 16/48. The logo keeps the
   default; it is 48 too. (`.fl-menu` is the corpus's, kept here so the whole
   rule reads in one place.) */
.fl-menu a:not(.fl-logo),
.fl-studio-sub,
.fl-notebook-link,
.fl-faq-link {
  --fl-lens: 37;
}

/* ── the travelling menu ───────────────────────────────────────────
   NOT IN THE DESIGN (Denis, 2026-08-05): the file draws the menu once, at the
   top of each frame. It has to stay in view down the whole page and stay
   legible over whatever passes under it. Fixed to the viewport at the design's
   own 69,39 — the canvas is centred, so the left offset is the gutter plus
   that 69 — and `effects.js` flips it to white over the photographs by
   measuring their luminance, never by guessing. */

/* Doubled up on purpose: `corpus2026.css` loads later and re-declares
   `.fl-menu { position: absolute }`, so this needs the extra class to win. */
.fl-menu.fl-menu-fixed {
  position: fixed;
  z-index: 4;
  left: calc(var(--fl-bleed) + 69 * var(--k));
}

/* Set per link by `effects.js`, from the luminance of what is actually behind
   that link — the bar is 1382 wide and the photographs under it start and stop
   anywhere along that span. */
.fl-menu a.is-light {
  color: #fff;
}

.fl-menu a.is-dark {
  color: #000;
}

/* The logo only: corpus2026.css gives the links `transition: opacity 0.25s`
   at the same specificity and later, so listing them here promised a colour
   fade they never had (final audit, 3-sep-2026 — kept as it renders). */
.fl-menu .fl-logo {
  transition: color 0.3s ease, opacity 0.25s ease;
}

/* The travelling menu is a desktop affordance; the phone layouts already pin
   their own bar and lay the links out in a row. */
@media (max-width: 799px) {
  .fl-menu.fl-menu-fixed {
    left: 0;
  }
}

/* Held for the one frame in which `effects.js` commits a new `--k`: every
   value that rides the canvas scale changes at once, and anything with a
   transition would otherwise animate towards it. */
.fl-resizing *,
.fl-resizing *::before,
.fl-resizing *::after {
  transition: none !important;
}
