diff --git a/README.Rmd b/README.Rmd
index eb0ef97..ee08524 100644
--- a/README.Rmd
+++ b/README.Rmd
@@ -14,7 +14,7 @@ knitr::opts_chunk$set(
options(balancing.quiet = TRUE)
```
-# balancing
+# balancing
[](https://github.com/r-causal/balancing/actions/workflows/R-CMD-check.yaml)
diff --git a/README.md b/README.md
index 5df2382..f3c2872 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
-# balancing
+# balancing
diff --git a/data-raw/logo/.gitignore b/data-raw/logo/.gitignore
new file mode 100644
index 0000000..4fba2ba
--- /dev/null
+++ b/data-raw/logo/.gitignore
@@ -0,0 +1 @@
+exports/
diff --git a/data-raw/logo/README.md b/data-raw/logo/README.md
new file mode 100644
index 0000000..9db4f61
--- /dev/null
+++ b/data-raw/logo/README.md
@@ -0,0 +1,13 @@
+# Package logo
+
+The text-free artwork was generated with the built-in image-generation tool.
+`prompt.txt` records the artwork prompt. `sticker.svg` adds editable typography
+and a border clipped to the 1.74 by 2 inch hexagon.
+
+Run `Rscript data-raw/logo/build.R` from the package root to export a 600 dpi
+print PNG and an SVG with outlined text to `exports/`, then install the README
+logo with `usethis::use_logo()`.
+
+The build requires the R packages base64enc, magick, rsvg, systemfonts, and
+usethis, plus Baskerville installed locally.
+The artwork, fonts, and print tools are not package runtime dependencies.
diff --git a/data-raw/logo/artwork.png b/data-raw/logo/artwork.png
new file mode 100644
index 0000000..c3f99f9
Binary files /dev/null and b/data-raw/logo/artwork.png differ
diff --git a/data-raw/logo/build.R b/data-raw/logo/build.R
new file mode 100644
index 0000000..01f9ec1
--- /dev/null
+++ b/data-raw/logo/build.R
@@ -0,0 +1,48 @@
+# Run from the package root: Rscript data-raw/logo/build.R
+args <- commandArgs(trailingOnly = FALSE)
+script <- sub("^--file=", "", args[grepl("^--file=", args)])
+source_dir <- dirname(normalizePath(script))
+root <- normalizePath(file.path(source_dir, "../.."))
+svg <- paste(readLines(file.path(source_dir, "sticker.svg")), collapse = "\n")
+families <- regmatches(svg, gregexpr('font-family="[^"]+"', svg))[[1]]
+families <- unique(sub('font-family="([^"]+)"', "\\1", families))
+missing <- setdiff(families, systemfonts::system_fonts()$family)
+if (length(missing)) {
+ stop(
+ "Install the source fonts before rebuilding: ",
+ paste(missing, collapse = ", ")
+ )
+}
+art <- base64enc::dataURI(
+ file = file.path(source_dir, "artwork.png"),
+ mime = "image/png"
+)
+svg <- sub("artwork.png", art, svg, fixed = TRUE)
+exports <- file.path(source_dir, "exports")
+dir.create(exports, showWarnings = FALSE)
+print_png <- file.path(exports, "logo.png")
+raster <- rsvg::rsvg_png(charToRaw(svg), width = 1044, height = 1200)
+magick::image_write(
+ magick::image_read(raster),
+ path = print_png,
+ format = "png",
+ density = "600x600"
+)
+
+# Outline the text separately to avoid resampling the embedded artwork.
+text_only <- gsub("]*/>", "", svg)
+text_only <- gsub("]*/>", "", text_only)
+text_only <- gsub(']*/>', "", text_only)
+print_svg <- file.path(exports, "logo.svg")
+rsvg::rsvg_svg(charToRaw(text_only), print_svg, width = 1740, height = 2000)
+outlined <- paste(readLines(print_svg), collapse = "\n")
+inner <- sub("(?s).*?\\s*$", "", inner, perl = TRUE)
+without_text <- gsub("(?s)]*>.*?", "", svg, perl = TRUE)
+combined <- sub("", paste0(inner, ""), without_text, fixed = TRUE)
+stopifnot(!grepl("]", combined))
+writeLines(combined, print_svg)
+
+usethis::proj_set(root)
+options(usethis.overwrite = TRUE)
+usethis::use_logo(print_png)
diff --git a/data-raw/logo/prompt.txt b/data-raw/logo/prompt.txt
new file mode 100644
index 0000000..b2b2950
--- /dev/null
+++ b/data-raw/logo/prompt.txt
@@ -0,0 +1 @@
+Use case: stylized-concept. A full-bleed fine-art OIL PAINTING for the balancing R package sticker. Portrait 3:4, no typography anywhere. This must look like a beautiful physical painting, NOT a logo, icon, isolated cutout, clipart, vector, digital mascot or product render. Paint an intimate still life in a quiet artist's studio: an old narrow balance beam made of weathered wood rests perfectly level across a simple dark triangular fulcrum on a worn pale stone table. A squat antique brass calibration weight with a round lifting knob sits at each end, matched size and equal distance so it is truly balanced. Show the objects in subtle three-quarter perspective with convincing depth, but the balance is visibly level. The brass weights have irregular tarnish and warm reflected light, not shiny perfect gold. A large soft window-light shape enters from upper left across a muted smoky teal wall, casting a long painterly shadow behind the arrangement. Rich thick brush marks and palette-knife scrapes, lost and found edges, delicious ochre, blue-black, pale celadon, dirty cream and warm sienna. Like a refined 20th-century European still-life painting reproduced in an art book. Strong expressive composition, asymmetrical lighting and architectural shadows, absolutely no heavy uniform outlines. The image fills the whole rectangular canvas with painted wall and tabletop, NO transparent background, no empty white backdrop. Main balance occupies central width x12%-88%, y30%-63%; lower quarter is painted quiet dark teal tabletop/shadow for real lettering later. No text, numbers, labels, watermark, border, hexagon, writing or decorative symbols.
diff --git a/data-raw/logo/sticker.svg b/data-raw/logo/sticker.svg
new file mode 100644
index 0000000..aca13b4
--- /dev/null
+++ b/data-raw/logo/sticker.svg
@@ -0,0 +1,6 @@
+
diff --git a/man/figures/logo.png b/man/figures/logo.png
new file mode 100644
index 0000000..fe907f2
Binary files /dev/null and b/man/figures/logo.png differ
diff --git a/pkgdown/favicon/apple-touch-icon.png b/pkgdown/favicon/apple-touch-icon.png
new file mode 100644
index 0000000..fb98c0f
Binary files /dev/null and b/pkgdown/favicon/apple-touch-icon.png differ
diff --git a/pkgdown/favicon/favicon-96x96.png b/pkgdown/favicon/favicon-96x96.png
new file mode 100644
index 0000000..8eb7cb6
Binary files /dev/null and b/pkgdown/favicon/favicon-96x96.png differ
diff --git a/pkgdown/favicon/favicon.ico b/pkgdown/favicon/favicon.ico
new file mode 100644
index 0000000..cf1c785
Binary files /dev/null and b/pkgdown/favicon/favicon.ico differ
diff --git a/pkgdown/favicon/favicon.svg b/pkgdown/favicon/favicon.svg
new file mode 100644
index 0000000..a0d73d7
--- /dev/null
+++ b/pkgdown/favicon/favicon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/pkgdown/favicon/site.webmanifest b/pkgdown/favicon/site.webmanifest
new file mode 100644
index 0000000..4ebda26
--- /dev/null
+++ b/pkgdown/favicon/site.webmanifest
@@ -0,0 +1,21 @@
+{
+ "name": "",
+ "short_name": "",
+ "icons": [
+ {
+ "src": "/web-app-manifest-192x192.png",
+ "sizes": "192x192",
+ "type": "image/png",
+ "purpose": "maskable"
+ },
+ {
+ "src": "/web-app-manifest-512x512.png",
+ "sizes": "512x512",
+ "type": "image/png",
+ "purpose": "maskable"
+ }
+ ],
+ "theme_color": "#ffffff",
+ "background_color": "#ffffff",
+ "display": "standalone"
+}
\ No newline at end of file
diff --git a/pkgdown/favicon/web-app-manifest-192x192.png b/pkgdown/favicon/web-app-manifest-192x192.png
new file mode 100644
index 0000000..3294e26
Binary files /dev/null and b/pkgdown/favicon/web-app-manifest-192x192.png differ
diff --git a/pkgdown/favicon/web-app-manifest-512x512.png b/pkgdown/favicon/web-app-manifest-512x512.png
new file mode 100644
index 0000000..35b0388
Binary files /dev/null and b/pkgdown/favicon/web-app-manifest-512x512.png differ
diff --git a/tests/testthat/_snaps/ipw.md b/tests/testthat/_snaps/ipw.md
index d0938e7..2b8a2d2 100644
--- a/tests/testthat/_snaps/ipw.md
+++ b/tests/testthat/_snaps/ipw.md
@@ -12,7 +12,8 @@
x2), method = bw_entropy(), estimand = "ate")
Outcome Model:
- Call: stats::glm(formula = formula, family = family, data = data, weights = .wts)
+ Call: stats::glm(formula = y ~ exposure, family = family, data = data,
+ weights = .wts)
Marginal estimates:
estimate std.err z ci.lower ci.upper conf.level
diff --git a/tests/testthat/test-ipw.R b/tests/testthat/test-ipw.R
index addcad5..086ba50 100644
--- a/tests/testthat/test-ipw.R
+++ b/tests/testthat/test-ipw.R
@@ -701,6 +701,8 @@ test_that("an ipw() result prints for a balancing fit", {
)
w <- as.numeric(stats::weights(fit))
outcome_mod <- fit_outcome(y ~ exposure, data, w, stats::binomial())
+ # R versions differ in whether glm() stores the formula symbol or its value.
+ outcome_mod$call$formula <- quote(y ~ exposure)
result <- ipw(fit, outcome_mod)