/* ---------- Dayhoff interactive logo: reaction-diffusion (Gray-Scott) ----------
   Full-bleed 16:9 block, capped to 100vh only on very large/tall screens
   where that height would otherwise exceed the viewport, right after the
   mark-animation video, right before the circular-images row. See
   "../jac-identity/Diseño Kinético e Interactivo.md" for the general
   kinetic/interactive-block technique and js/dayhoff-reaction.js for the
   simulation itself.

   What it is: a Gray-Scott reaction-diffusion simulation — two virtual
   "chemicals" (U, V) diffusing across a grid and reacting where they meet
   — seeded with the exact silhouette of the Dayhoff logo (V saturated
   inside it, U saturated outside). The reaction can only start where the
   two chemicals actually touch, i.e. right at the logo's own edge, so the
   organic pattern is born from the mark itself: it eats inward (breaking
   up the solid shape) and spreads outward (spilling into the surrounding
   field) at once. This is the client's own reference (three images of
   Turing/Gray-Scott patterns, including the Pearson pattern-type chart)
   — not a metaphorical "ferrofluid", the literal reaction-diffusion model
   whose parameters (F, k, Du, Dv) those references show.

   Runs on a <canvas> via WebGL2 (native browser API, not an external
   library — respects the project's vanilla-JS rule) since a per-pixel PDE
   over a large grid, every frame, isn't realistic in plain JS.

   Does NOT use the studio's "Piezas" system (client correction — Piezas is
   for buttons/panels elsewhere) beyond reusing the icon-reveals-a-slider
   *interaction* already built for MSP's landscape-playground.js and the
   first Dayhoff attempt.

   Colours are the brand palette's lightest/darkest tones (Light Grey
   #EDEEE9 / Dark Brown #352605), not literal black/white and not the
   site's light/dark toggle — the rest of the brand palette is deferred to
   a later round (client request: "empecemos en blanco y negro, los
   colores los añadimos más tarde" — "blanco y negro" meaning these two
   tones, confirmed after the fact). */

.dayhoff-reaction {
  position: relative;
  width: 100%;
  /* Back to the original full-bleed 16:9 (client correction — a flat
     100vh/100vw block made it too tall/narrow proportioned relative to the
     logo on normal screens, and the logo effectively disappeared into a
     tiny centred fraction of a much bigger canvas). aspect-ratio still
     drives the height on ordinary screens; max-height only kicks in once
     that 16:9 height would exceed the viewport (very large/tall screens),
     capping it to 100vh there instead of letting the block run taller than
     one screen. */
  aspect-ratio: 16 / 9;
  max-height: 100vh;
  max-height: 100dvh;
  overflow: hidden;
  background: #ededea; /* Dayhoff light grey — matches the sim's own "ink-free" tone, painted before WebGL's first frame */
  /* Client-found bug: without its own explicit margin, the gap above came
     from the preceding .project-media's ambient padding-bottom (90px,
     desktop) and the gap below came from the following .project-media's
     ambient margin-top (64px) — visibly more space above than below.
     Owning both margins directly (and cancelling those two ambient rules
     in css/project.css) makes it exactly 100px on both sides, always. */
  margin-top: var(--grid-gutter);
  margin-bottom: var(--grid-gutter);
}

@media (min-width: 1181px) {
  .dayhoff-reaction {
    margin-top: 100px;
    margin-bottom: 100px;
  }
}

.dayhoff-reaction-canvas {
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none;
  cursor: pointer;
}

/* ---------- Control panel ----------
   Same visual mechanism as the landscape-playground / first Dayhoff
   attempt's panel (icon row, single shared reveal slider, hover
   name-tag, instant-toggle icon) — duplicated, not shared, per the
   kinetic-design guide's "each block is its own file pair" rule. */
/* Deliberately opaque-ish and neutral (not a low-alpha tint of the
   simulation's own ink colour) — the panel has to stay legible no matter
   what the live pattern underneath is doing, including a near-solid dark
   fill (client correction: the buttons "se pierden" when the field
   behind them turned dark). A light, high-contrast chip that never
   depends on reading the canvas behind it is the reliable fix, rather
   than trying to track the field's own colour the way landscape-
   playground's panel does. */
.dayhoff-reaction-panel {
  position: absolute;
  top: var(--grid-margin);
  right: var(--grid-margin);
  z-index: 2;
  width: 266px; /* 7 icons × 38px */
  display: flex;
  flex-direction: column;
  --dr-glass: rgba(255, 255, 255, 0.92);
  --dr-ink: rgba(20, 20, 20, 0.92);
  --dr-ink-dim: rgba(20, 20, 20, 0.55);
  --dr-lift-14: rgba(20, 20, 20, 0.08);
  --dr-lift-5: rgba(20, 20, 20, 0.05);
  --dr-track: rgba(20, 20, 20, 0.3);
  --dr-accent: rgba(20, 20, 20, 0.9);
}

.dr-piece {
  border-radius: 5px;
  background: var(--dr-glass);
  backdrop-filter: blur(16px) saturate(1.4);
  -webkit-backdrop-filter: blur(16px) saturate(1.4);
}

.dr-icon-line {
  position: relative;
}

.dr-icon-tooltip {
  position: absolute;
  top: 0;
  bottom: 0;
  right: 100%;
  display: flex;
  align-items: center;
  padding: 0 14px;
  white-space: nowrap;
  border-radius: 5px;
  background: var(--dr-glass);
  backdrop-filter: blur(16px) saturate(1.4);
  -webkit-backdrop-filter: blur(16px) saturate(1.4);
  color: var(--dr-ink);
  font-size: 14px;
  font-weight: 500;
  pointer-events: none;
  clip-path: inset(0 0 0 100%);
  transition: clip-path 0.25s cubic-bezier(0.3, 0, 0.2, 1);
}

.dr-icon-tooltip.is-visible {
  clip-path: inset(0 0 0 0%);
}

.dr-icon-row {
  display: flex;
  width: 266px;
  overflow: hidden;
}

.dr-icon-btn {
  position: relative;
  flex: 0 0 38px;
  width: 38px;
  height: 38px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: transparent;
  color: var(--dr-ink-dim);
  cursor: pointer;
  overflow: hidden;
  transition:
    background-color 0.15s ease-in-out,
    color 0.15s ease-in-out;
}

.dr-icon-btn svg {
  width: 18px;
  height: 18px;
  display: block;
}

.dr-icon-btn:hover {
  background: var(--dr-lift-5);
  color: var(--dr-ink);
}

.dr-icon-btn.is-active {
  background: var(--dr-lift-14);
  color: var(--dr-ink);
}

/* Shading icon — client correction: at rest, the same currentColor alpha
   (0.55, --dr-lift-14's dimmed tone) read lighter as the semicircle's flat
   fill than as the ring's own thin stroke, even though both use the exact
   same colour value — a large flat wash of a colour reads lighter than a
   thin line of the identical colour (area/contrast perception, not a real
   colour mismatch). Bumping just the fill's alpha compensates for that;
   hover/active already read as matching, so they fall back to plain
   currentColor there. Client request: hovering also flips which side is
   dark/light — a plain horizontal mirror, snapping instantly (client
   correction: no transition here on purpose, it read as an unwanted
   animation — the icon should just be in one state or the other). */
.dr-shading-btn path {
  fill: rgba(20, 20, 20, 0.75);
}

.dr-shading-btn:hover path,
.dr-shading-btn.is-active path {
  fill: currentColor;
}

.dr-shading-btn:hover svg {
  transform: scaleX(-1);
}

/* Invert-colours icon — ported from landscape-playground.css's
   .lp-color-btn/.lp-color-dot/.lp-color-wipe: the button itself is a solid
   swatch, the dot matches the button's own resting colour, and clicking
   advances to the next colour pair in js's COLOR_PAIRS, wrapping around
   (js's triggerColorCycle() drives the wipe-then-dot-grows-in animation).
   Client correction: the swatch used to show the pair's *ink* colour (the
   canvas's dominant, usually-larger colour) and the dot its *bg* — with a
   mostly-dark pattern that made the button nearly disappear into the
   canvas behind it. applyColorPair() now feeds the swatch the pair's *bg*
   and the dot its *ink* instead — deliberately the opposite of the
   canvas's own uBg/uInk mapping in renderComposite(), which is untouched.
   Resting colours come from --dr-invert-swatch/--dr-invert-dot (set on the
   button by applyColorPair(), inherited by the dot/wipe below it) — a real
   stylesheet rule, exactly like MSP's own var(--lp-fg)/var(--lp-bg) — not a
   value written straight onto .style.background. That's deliberate: the
   click animation freezes the *old* colour onto .style.backgroundColor (a
   different property) and clears it once done, which only reveals the
   correct new colour again because that's a separate mechanism from this
   rule — mixing the two on the same property is what caused the
   button/dot to go blank instead of showing the frozen colour (client-
   found bug). No background-color transition here on purpose — the click
   animation is the only thing that's ever supposed to move this button's
   colours; a CSS transition left in would replay every time the frozen
   inline colour is cleared at the end of that animation, reading as a
   flash of the old colour. */
.dr-invert-btn,
.dr-invert-btn:hover {
  background: var(--dr-invert-swatch);
  /* Client-found bug: this is the row's last (rightmost) button, flush
     against .dr-icon-row's own rounded-and-clipped right edge (it shares
     .dr-piece's 5px radius and clips its children to it). A square swatch
     butted against that curve left two tiny corners of the row's own glass
     background exposed at top-right/bottom-right — invisible against pale
     default colours, but a visible pale sliver once the swatch could be any
     one of the five brand colours. Rounding this button's own two outer
     corners to match removes the gap. */
  border-radius: 0 5px 5px 0;
  transition: color 0.15s ease-in-out;
}

.dr-invert-dot,
.dr-invert-wipe {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 14px;
  height: 14px;
  margin: -7px 0 0 -7px;
  border-radius: 50%;
  background: var(--dr-invert-dot);
}

.dr-invert-dot {
  z-index: 2;
  transition: transform 0.15s ease;
}

.dr-invert-btn:hover .dr-invert-dot {
  transform: scale(1.15);
}

.dr-invert-wipe {
  z-index: 1;
  transform: scale(0);
  pointer-events: none;
}

.dr-reveal {
  position: relative;
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.4s cubic-bezier(0.3, 0, 0.2, 1);
}

.dr-reveal.is-open {
  grid-template-rows: 1fr;
}

.dr-reveal-inner {
  overflow: hidden;
  min-height: 0;
}

.dr-slider-row {
  padding: 12px 16px;
}

/* Pulse group: three sliders sharing one icon/reveal — this is MSP's own
   Ripple group mechanism (css/landscape-playground.css's
   .lp-ripple-group/.lp-mini-slider-row), copied exactly rather than
   reinvented. Each row carries the .dr-slider-row class directly (its own
   12px/16px padding, 40px tall total) with zero margin between rows —
   client-found bug: an earlier version put the padding on the *group*
   instead and used a 12px inter-row margin, which made each row's real
   height only the bare 16px input, visibly thinner than MSP's. Each
   sub-slider is named by its own hover tooltip (see .dr-pulse-tooltip
   below, fixed at the same 40px row height), not a permanent label —
   same grow-from-anchor animation the icon tooltips use, anchored to
   that row. */

/* .dr-icon-tooltip sets top:0;bottom:0 to span a whole icon button — this
   row-anchored variant overrides both with an explicit per-row top/height
   set inline in JS instead, so it lines up with its own slider row rather
   than the whole group. */
.dr-pulse-tooltip {
  top: auto;
  bottom: auto;
}

.dr-slider-row input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  display: block;
  width: 100%;
  height: 16px;
  margin: 0;
  background: transparent;
  cursor: pointer;
}

.dr-slider-row input[type="range"]::-webkit-slider-runnable-track {
  height: 100%;
  background: linear-gradient(var(--dr-track), var(--dr-track)) no-repeat center / 100% 1px;
}

.dr-slider-row input[type="range"]::-moz-range-track {
  height: 100%;
  background: linear-gradient(var(--dr-track), var(--dr-track)) no-repeat center / 100% 1px;
}

.dr-slider-row input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 1px;
  height: 12px;
  background: var(--dr-accent);
  margin-top: 2px;
  transition: transform 0.15s ease;
}

.dr-slider-row input[type="range"]::-moz-range-thumb {
  width: 1px;
  height: 12px;
  border: none;
  background: var(--dr-accent);
  transition: transform 0.15s ease;
}

.dr-slider-row input[type="range"]:active::-webkit-slider-thumb {
  transform: scaleX(3);
}

.dr-slider-row input[type="range"]:active::-moz-range-thumb {
  transform: scaleX(3);
}

.dayhoff-reaction-logo-source {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

/* Mobile and tablet: 75vh tall instead of 16:9 (client request). */
@media (max-width: 1180px) {
  .dayhoff-reaction {
    aspect-ratio: auto;
    max-height: none;
  }

  /* Same as MSP's panel: centred at the bottom of the block, and no
     name-tag on tap (client request). */
  .dayhoff-reaction-panel {
    top: auto;
    bottom: var(--grid-margin);
    left: 0;
    right: 0;
    margin-inline: auto;
  }

  .dayhoff-reaction-panel [class*="tooltip"] {
    display: none;
  }
}

@media (max-width: 700px) {
  .dayhoff-reaction-panel {
    width: 221.2px; /* 7 icons × 31.6px */
  }

  .dr-icon-row {
    width: 221.2px;
  }

  .dr-icon-btn {
    flex-basis: 31.6px;
    width: 31.6px;
    height: 32px;
  }

  .dr-icon-btn svg {
    width: 15px;
    height: 15px;
  }

  .dr-icon-tooltip {
    height: 32px;
  }

  .dr-slider-row {
    padding: 6px 12px 8px;
  }
}

/* Mobile and tablet: everything touchable is exactly 44px (the minimum),
   made of squares: --ps is one button's side. After the ≤700px block above.
   The logo is centred between the top of the block and the top of the closed
   panel, in js/dayhoff-reaction.js. */
@media (max-width: 1180px) {
  .dayhoff-reaction {
    --ps: 44px;
    height: 75vh;
  }

  .dayhoff-reaction-panel {
    width: calc(7 * var(--ps));
  }

  .dr-icon-row {
    width: calc(7 * var(--ps));
  }

  .dr-icon-btn {
    flex-basis: var(--ps);
    width: var(--ps);
    height: var(--ps);
  }

  .dr-icon-btn svg {
    width: calc(var(--ps) * 18 / 38); /* the desktop proportion */
    height: calc(var(--ps) * 18 / 38);
  }

  .dr-slider-row {
    padding: 0 16px;
  }

  .dr-slider-row input[type="range"] {
    height: var(--ps);
  }

  .dr-slider-row input[type="range"]::-webkit-slider-thumb {
    margin-top: calc((var(--ps) - 12px) / 2); /* same 12px line as desktop, centred */
  }
}


/* Mobile and tablet: the controls hide behind a square button, bottom right
   (client request). Closed: only the 44px button (two lines). Open: the lines
   turn into a cross and the panel opens right-to-left from the button's right
   edge (300ms), then, after it, the extra rows open upwards (250ms; the word
   row in MSP). Closing is the reverse, top to bottom: open slider and word row
   fold, then the panel slides back into the button (300ms) with no pause:
   js sets --menu-close-delay to however long the rows that are open take to fold. It
   always sits right above the button, never inside it. */
.dr-menu-toggle {
  display: none;
}

@media (max-width: 1180px) {
  .dr-menu-toggle {
    display: block;
    position: absolute;
    right: var(--grid-margin);
    bottom: var(--grid-margin);
    z-index: 3;
    width: var(--ps);
    height: var(--ps);
    padding: 0;
    border: none;
    border-radius: 5px;
    background: var(--dr-glass, rgba(255, 255, 255, 0.92)); /* tokens live on the panel, not the root */
    backdrop-filter: blur(16px) saturate(1.4);
    -webkit-backdrop-filter: blur(16px) saturate(1.4);
    cursor: pointer;
    transition: background-color 0.3s ease;
  }

  .dr-menu-toggle span {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 20px;
    height: 1.5px;
    margin: -0.75px 0 0 -10px;
    background: var(--dr-ink, rgba(20, 20, 20, 0.92));
    transition:
      transform 0.3s cubic-bezier(0.3, 0, 0.2, 1),
      background-color 0.3s ease;
  }

  .dr-menu-toggle span:nth-child(1) {
    transform: translateY(-3.5px);
  }

  .dr-menu-toggle span:nth-child(2) {
    transform: translateY(3.5px);
  }

  .dayhoff-reaction.is-menu-open .dr-menu-toggle span:nth-child(1) {
    transform: rotate(45deg);
  }

  .dayhoff-reaction.is-menu-open .dr-menu-toggle span:nth-child(2) {
    transform: rotate(-45deg);
  }

  .dayhoff-reaction-panel {
    left: auto;
    right: var(--grid-margin);
    margin-inline: 0;
    bottom: calc(var(--grid-margin) + var(--ps));
    align-items: flex-end;
    overflow: hidden;
    width: 0;
    visibility: hidden;
    transition:
      width 0.3s cubic-bezier(0.3, 0, 0.2, 1) var(--menu-close-delay, 0s),
      visibility 0s linear calc(var(--menu-close-delay, 0s) + 0.3s);
  }

  .dayhoff-reaction.is-menu-open .dayhoff-reaction-panel {
    width: calc(7 * var(--ps));
    visibility: visible;
    transition:
      width 0.3s cubic-bezier(0.3, 0, 0.2, 1),
      visibility 0s;
  }

  .dayhoff-reaction-panel > * {
    flex: 0 0 auto;
    width: calc(7 * var(--ps));
  }
}

/* Menu button top right (client request): the panel opens downwards from it,
   right-aligned, and the logo is centred in the whole block, ignoring the menu
   (js/dayhoff-reaction.js). */
@media (max-width: 1180px) {
  .dr-menu-toggle {
    top: var(--grid-margin);
    bottom: auto;
  }

  .dayhoff-reaction-panel {
    top: calc(var(--grid-margin) + var(--ps));
    bottom: auto;
  }
}

/* The panel opens to the LEFT of the menu button, in the button's own row
   (client request), so 7 icons + the button = 8 squares must fit between the
   page margins: the side is 44px, a hair less on a narrow phone (350 / 8 at
   390px wide). The extra rows (sliders) open below the icon row. */
@media (max-width: 1180px) {
  .dayhoff-reaction {
    --ps: min(44px, calc((100vw - 2 * var(--grid-margin)) / 8));
  }

  .dayhoff-reaction-panel {
    top: var(--grid-margin);
    right: calc(var(--grid-margin) + var(--ps));
  }
}

/* Mobile and tablet: 2 × the page's side margin above and below (40px phone,
   50px tablet), client request. The neighbours' ambient spacing is already
   cancelled in css/project.css. Desktop keeps its own 100px. */
@media (max-width: 1180px) {
  .dayhoff-reaction {
    margin-top: calc(2 * var(--grid-margin));
    margin-bottom: calc(2 * var(--grid-margin));
  }
}
