// Gradient landscape: hard box vs Gumbel box (1D).
// Compile: typst compile gradient_landscape.typ gradient_landscape.png --ppi 500 --root ..
#import "../_diagram-style.typ": *
#set page(width: 680pt, height: 210pt, margin: 12pt)
#set text(font: base-font, size: label-size)

#import "@preview/cetz:0.3.4"

#cetz.canvas(length: 1pt, {
  import cetz.draw: *

  // --- Left panel: Hard box volume ---
  let ox = 40
  let oy = 36
  let pw = 180
  let ph = 130

  // Axes
  line((ox, oy), (ox + pw + 10, oy), stroke: 0.8pt + border)
  line((ox, oy), (ox, oy + ph + 10), stroke: 0.8pt + border)

  // Axis labels
  content((ox + pw / 2, oy - 20), text(min-size, fill: fg3)[#h(-24pt) $arrow.l$ overlap #h(8pt) gap $arrow.r$])
  content((ox - 20, oy + ph / 2), angle: 90deg, text(annot-size, fill: fg3)[volume])

  // Title
  content((ox + pw / 2, oy + ph + 22), text(15pt, weight: "bold", fill: fg)[Hard box])

  let scale-y = ph / 3.5
  let mid-x = ox + pw / 2

  // Zero marker on x-axis
  content((mid-x, oy - 8), text(min-size, fill: fg3)[0])

  // Overlap region: volume decreases linearly
  line((ox, oy + 3 * scale-y), (mid-x, oy), stroke: 1.6pt + blue.stroke)
  // Disjoint region: volume = 0
  line((mid-x, oy), (ox + pw, oy), stroke: 1.6pt + blue.stroke)

  // Zero-gradient shaded region
  rect((mid-x + 4, oy - 1), (ox + pw - 4, oy + 20),
    stroke: none, fill: red.fill.transparentize(30%))
  content((mid-x + (pw / 2 - 4) / 2 + 4, oy + 10),
    text(min-size, fill: red.stroke, weight: "bold")[gradient = 0])

  // Kink at origin
  circle((mid-x, oy), radius: 3pt, stroke: 1pt + border, fill: white)

  // --- Right panel: Gumbel box (softplus) ---
  let ox2 = 290

  // Axes
  line((ox2, oy), (ox2 + pw + 10, oy), stroke: 0.8pt + border)
  line((ox2, oy), (ox2, oy + ph + 10), stroke: 0.8pt + border)

  // Axis labels
  content((ox2 + pw / 2, oy - 20), text(min-size, fill: fg3)[#h(-24pt) $arrow.l$ overlap #h(8pt) gap $arrow.r$])
  content((ox2 - 20, oy + ph / 2), angle: 90deg, text(annot-size, fill: fg3)[expected volume])

  // Title
  content((ox2 + pw / 2, oy + ph + 22), text(15pt, weight: "bold", fill: fg)[Gumbel box])

  // Softplus curve
  let pts = ()
  let n = 120
  for i in range(n + 1) {
    let t = i / n
    let gap = -3 + 6 * t
    let arg = -gap * 1.3
    let vol = if arg > 15 { arg } else { calc.ln(1 + calc.exp(arg)) }
    let px = ox2 + (t * pw)
    let py = oy + vol * scale-y
    if py > oy + ph { py = oy + ph }
    pts.push((px, py))
  }

  for i in range(pts.len() - 1) {
    line(pts.at(i), pts.at(i + 1), stroke: 1.6pt + green.stroke)
  }

  // Annotation: gradient != 0
  content((ox2 + pw * 0.68, oy + 28),
    text(min-size, fill: green.stroke, weight: "bold")[gradient $!= 0$])

  // Tangent at gap ~ 1.5
  let tg = 1.5
  let ta = -tg * 1.3
  let tv = calc.ln(1 + calc.exp(ta))
  let ty = oy + tv * scale-y
  let tx = ox2 + ((tg + 3) / 6) * pw
  let deriv = -1.3 * (1 / (1 + calc.exp(ta)))
  let dl = 18
  line((tx - dl, ty - deriv * dl * scale-y / (pw / 6)),
       (tx + dl, ty + deriv * dl * scale-y / (pw / 6)),
    stroke: (paint: green.stroke, thickness: 1pt, dash: "dashed"))
})
