/* -----------------------------------------------------------------------------
 * cleanamsterdam.nl — site.css
 *
 * THE OFFICE DESIGN — teal + orange + glass.
 *
 * Named components rather than utility classes, so the markup in
 * sitegen/templates/*.html says what a thing IS rather than how it looks.
 *
 * Pages still on the old renderer link legacy.css instead. When the last one
 * moves over, legacy.css goes and this is the whole of it.
 *
 * Rules are scoped to `.shell` so the two stylesheets stay safe to load
 * together during the migration.
 * ---------------------------------------------------------------------------*/

/* ---- tokens -------------------------------------------------------------- */

:root {
  --radius: 1rem;

  --background: #F5FBFB;
  --foreground: #071F20;
  --card: #FFFFFF;
  --card-foreground: #071F20;

  /* Orange. Action, foreground, every CTA. */
  --primary: #F97D14;
  --primary-foreground: #FFFBF4;

  --secondary: #DEF2F0;
  --muted-foreground: #58747E;

  /* Teal. Back, chrome, headings. */
  --accent: #055959;
  --teal-deep: #003738;

  --border: #D3E1E1;
  --check: #0FA05C;

  /* Glass. "more see-through, less opaque, and the blur slightly less." */
  --glass: rgb(255 255 255 / 0.72);
  --glass-strong: rgb(255 255 255 / 0.88);
  --glass-border: rgb(255 255 255 / 0.55);
  --glass-blur: 24px;

  --shadow-glass: 0 24px 60px -24px rgb(0 28 31 / 0.55);
  --shadow-cta: 0 14px 34px -12px rgb(241 106 0 / 0.65);

  --font-display: "Bricolage Grotesque", ui-sans-serif, system-ui, sans-serif;
  --font-body: "Inter Tight", ui-sans-serif, system-ui, sans-serif;

  --measure: 64rem;   /* the column every section aligns to */
}

/* ---- shell: page base ---------------------------------------------------- */

/* legacy.css opens with a global `* { box-sizing: border-box }` and every rule
   below was written expecting it. Splitting the two stylesheets took it away:
   the fixed header then measured its width before its own padding and hung
   18px off the right of the viewport at every screen size — invisibly, because
   a fixed element does not lengthen the document, so nothing scrolled and no
   overflow check noticed. */
.shell,
.shell *,
.shell *::before,
.shell *::after { box-sizing: border-box; }

/* The other half of the same loss, and the one that got away.
 *
 * legacy.css set `body { margin: 0 }` on line 37 and every rule here assumed
 * it. Without it the browser's own 8px body margin comes back, so a design
 * built on full-bleed photography sat inside a white frame on all four sides.
 *
 * It survived because it does not look like a bug. A missing box-sizing hung
 * the header off the viewport and read as broken; 8px of white reads as a
 * border somebody chose. Scoped to html/body rather than .shell, because it
 * is the document that carries the margin, not the wrapper inside it. */
html { -webkit-text-size-adjust: 100%; }
body { margin: 0; }

.shell {
  background: var(--background);
  color: var(--foreground);
  font-family: var(--font-body);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

.shell h1,
.shell h2,
.shell h3 { font-weight: 800; letter-spacing: -0.025em; }

.shell a { color: inherit; text-decoration: none; }

.shell img,
.shell svg { display: block; max-width: 100%; }

.shell .measure {
  margin-inline: auto;
  width: 100%;
  max-width: var(--measure);
}

/* ---- nav: the floating glass pill ---------------------------------------- */

.shell .nav {
  position: fixed;
  inset-inline: 0;
  top: 0;
  z-index: 50;
  padding-inline: 1rem;
}

.shell .nav__bar {
  margin-top: 0.75rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  border-radius: 9999px;
  border: 1px solid var(--glass-border);
  background: var(--glass);
  padding: 0.625rem 1rem;
  box-shadow: var(--shadow-glass);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
}

/* min-width: 0 because a flex item defaults to min-width: auto and refuses to
   shrink below its own text. The brand gives way and the call to action does
   not — losing a letter of the wordmark costs nothing, losing the button costs
   the page. Belt and braces alongside the overflow fix below: this keeps the
   bar inside the viewport whatever a page puts in it. */
.shell .nav__brand {
  font-family: var(--font-display);
  font-size: 1rem;
  font-weight: 800;
  letter-spacing: -0.025em;
  color: var(--foreground);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.shell .nav__right { display: flex; align-items: center; gap: 0.75rem; flex: none; }

.shell .nav__langs {
  display: none;
  align-items: center;
  gap: 0.375rem;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--muted-foreground);
}

.shell .nav__lang { transition: color 0.15s; }
.shell .nav__lang:hover { color: var(--foreground); }
.shell .nav__lang--current { color: var(--foreground); }

/* ---- btn ------------------------------------------------------------------
 * Orange is action. The WhatsApp mark rides inside the orange button rather
 * than replacing it: "the Book via WhatsApp button should be the orange color,
 * and then we make the WhatsApp color smaller but still present, inside it."
 * -------------------------------------------------------------------------- */

.shell .btn {
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
  border-radius: 9999px;
  padding: 0.875rem 1.5rem;
  font-weight: 600;
  transition: transform 0.15s, background-color 0.15s;
}

.shell .btn svg { height: 1.25rem; width: 1.25rem; flex: none; }

.shell .btn--primary {
  background: var(--primary);
  color: var(--primary-foreground);
  box-shadow: var(--shadow-cta);
}
.shell .btn--primary:hover { transform: translateY(-0.125rem); }

.shell .btn--glass {
  border: 1px solid var(--glass-border);
  background: var(--glass-strong);
  color: var(--foreground);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
}
.shell .btn--glass:hover { background: var(--secondary); }

/* Header-sized: small enough to sit inside the pill. */
.shell .btn--compact {
  gap: 0.5rem;
  padding: 0.5rem 1rem;
  font-size: 0.875rem;
}
.shell .btn--compact svg { height: 1rem; width: 1rem; }

/* Closing-CTA-sized. */
.shell .btn--lg { padding: 1rem 2rem; font-size: 1.125rem; }
.shell .btn--lg svg { height: 1.5rem; width: 1.5rem; }

/* ---- scene ----------------------------------------------------------------
 * A full-height photograph with a glass panel resting on it.
 *
 * "why have a hero at all, why not let sections slide into each other as you
 * scroll down" — so there is no hero type. Every scene is the same shape and
 * they overlap: each is pulled up 22px over the one before and its top edge is
 * masked away, which is what makes the seam disappear. For the overlap to read
 * correctly the earlier scene has to paint on top, so --scene-z counts DOWN
 * the page. It is set per section in the template, where the running order is
 * visible.
 * -------------------------------------------------------------------------- */

.shell .scene {
  position: relative;
  isolation: isolate;
  z-index: var(--scene-z, 1);
  min-height: 92svh;
  overflow: hidden;
  margin-top: -22px;
}

/* The first scene has nothing above it to slide under. */
.shell main > .scene:first-of-type { margin-top: 0; }

.shell .scene__photo {
  position: absolute;
  inset: 0;
  z-index: -10;
  -webkit-mask-image: linear-gradient(transparent 0, #000 22px, #000 calc(100% - 22px), transparent 100%);
  mask-image: linear-gradient(transparent 0, #000 22px, #000 calc(100% - 22px), transparent 100%);
}

.shell main > .scene:first-of-type .scene__photo {
  -webkit-mask-image: linear-gradient(#000 0, #000 calc(100% - 22px), transparent 100%);
  mask-image: linear-gradient(#000 0, #000 calc(100% - 22px), transparent 100%);
}

/* The scrim only has to lift text off the photograph, so it stays absent
   across the top half: "the see-through can be more see-through." */
.shell .scene__photo::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    transparent 30%,
    rgb(7 31 32 / 0.15) 55%,
    rgb(7 31 32 / 0.55) 100%
  );
}

.shell .scene__img {
  position: absolute;
  inset: 0;
  height: 100%;
  width: 100%;
  object-fit: cover;
}

.shell .scene__body {
  position: relative;
  display: flex;
  min-height: 92svh;
  flex-direction: column;
  justify-content: flex-end;
  padding: 7rem 1rem 2.5rem;
}

/* Holds the overlap open so the next scene has something to slide under. */
.shell .scene__spacer { height: 0.75rem; flex: none; }

/* A section that is not a photograph — the district picker. */
.shell .band {
  position: relative;
  z-index: var(--scene-z, 1);
  background: var(--background);
  padding: 5rem 1rem;
}

/* ---- panel: the glass the copy sits in ----------------------------------- */

.shell .panel {
  border-radius: calc(var(--radius) + 12px);
  border: 1px solid var(--glass-border);
  background: var(--glass);
  padding: 1.5rem;
  box-shadow: var(--shadow-glass);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
}

.shell .panel__actions { display: flex; flex-direction: column; gap: 1rem; }
.shell .panel__foot { margin-top: 1.5rem; }

/* Section headings. Free-standing so a band (no glass) and a panel (glass) set
   type identically — the container decides the backdrop, not the scale. */
.shell .title { font-size: 1.5rem; }
.shell .title--lead { font-size: 1.875rem; }
.shell .sub { margin-top: 0.5rem; color: var(--muted-foreground); }
.shell .lede { margin-top: 0.75rem; max-width: 36rem; color: var(--muted-foreground); }

/* The location line. "orange should not be the accent in the location pill." */
.shell .region {
  margin-top: 1.25rem;
  border-top: 1px solid var(--border);
  padding-top: 1rem;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--muted-foreground);
}

/* ---- hero: badge and h1, above the panel rather than inside it ------------ */

.shell .hero { margin-bottom: 1.5rem; max-width: 42rem; }

.shell .hero__title {
  margin-top: 1rem;
  font-size: 2.25rem;
  line-height: 1.05;
  color: var(--primary-foreground);
  text-shadow: 0 2px 12px rgb(0 0 0 / 0.45);
}

/* District spokes: one line of local colour under the h1. */
.shell .hero__blurb {
  margin-top: 0.75rem;
  max-width: 36rem;
  color: var(--primary-foreground);
  text-shadow: 0 1px 8px rgb(0 0 0 / 0.5);
}

/* "offerte binnen 1 werkdag should not be orange." */
.shell .badge {
  display: inline-flex;
  align-items: center;
  border-radius: 9999px;
  border: 1px solid var(--glass-border);
  background: var(--glass);
  padding: 0.375rem 0.875rem;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--foreground);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}

/* ---- checklist: what a standard round covers ----------------------------- */

.shell .checklist {
  margin-top: 1.5rem;
  display: grid;
  gap: 0.75rem;
  list-style: none;
  padding: 0;
}

.shell .checklist__item {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  font-size: 0.875rem;
}

.shell .checklist__item svg {
  margin-top: 0.125rem;
  height: 1.25rem;
  width: 1.25rem;
  flex: none;
  color: var(--check);
}

/* ---- cards: how we work -------------------------------------------------- */

.shell .cards { margin-top: 1.5rem; display: grid; gap: 1rem; }

.shell .card {
  border-radius: calc(var(--radius) + 8px);
  border: 1px solid var(--border);
  background: var(--card);
  padding: 1.25rem;
  color: var(--card-foreground);
}

.shell .card__title { font-size: 1.125rem; font-weight: 700; }
.shell .card__body { margin-top: 0.5rem; font-size: 0.875rem; color: var(--muted-foreground); }

/* ---- districts: the spoke picker, hub only ------------------------------- */

.shell .districts { margin-top: 2rem; display: grid; gap: 1rem; }

.shell .district {
  display: block;
  border-radius: calc(var(--radius) + 8px);
  border: 1px solid var(--border);
  background: var(--card);
  padding: 1.5rem;
  transition: transform 0.15s, border-color 0.15s;
}
.shell .district:hover { transform: translateY(-0.125rem); border-color: var(--primary); }

.shell .district__title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 1.125rem;
  font-weight: 700;
  color: var(--accent);
}

.shell .district__arrow { color: var(--primary); transition: transform 0.15s; }
.shell .district:hover .district__arrow { transform: translateX(0.25rem); }

.shell .district__body { margin-top: 0.5rem; font-size: 0.875rem; color: var(--muted-foreground); }

/* ---- footer -------------------------------------------------------------- */

.shell .footer {
  background: var(--teal-deep);
  padding: 3rem 1rem;
  color: var(--primary-foreground);
}

.shell .footer__inner > * + * { margin-top: 1rem; }

.shell .footer__brand { font-family: var(--font-display); font-size: 1.125rem; font-weight: 700; }
.shell .footer__brand a:hover { text-decoration: underline; text-underline-offset: 4px; }

.shell .footer__nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1rem;
  font-size: 0.875rem;
  opacity: 0.9;
}
.shell .footer__nav a:hover { text-decoration: underline; }

.shell .footer__region { font-size: 0.875rem; opacity: 0.75; }
.shell .footer__legal { font-size: 0.8125rem; opacity: 0.6; }
.shell .footer__legal a:hover { text-decoration: underline; }

/* The cookie-settings control is a button because it does something rather
   than going somewhere, but it has to read as part of the link row. */
.shell .footer__linklike,
.shell .prose__linklike {
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
.shell .footer__linklike:hover,
.shell .prose__linklike:hover { text-decoration: underline; }

/* ---- >= 640px: "isn't there a sane way to do this for all, responsive" ---- */

@media (min-width: 640px) {
  .shell .nav { padding-inline: 1.5rem; }
  .shell .nav__bar { padding-inline: 1.25rem; }
  .shell .nav__langs { display: flex; }

  .shell .scene,
  .shell .scene__body { min-height: 88svh; }
  .shell .scene__body { padding: 7rem 1.5rem 3.5rem; }
  .shell .scene__spacer { height: 0.5rem; }
  .shell .band { padding-inline: 1.5rem; }

  .shell .panel { padding: 2.25rem; }
  .shell .panel__actions { flex-direction: row; align-items: center; }

  .shell .title { font-size: 2.25rem; }
  .shell .title--lead { font-size: 3rem; }

  .shell .hero__title { font-size: 3.75rem; }

  .shell .checklist { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .shell .checklist__item { font-size: 1rem; }
  .shell .cards,
  .shell .districts { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  .shell .footer { padding-inline: 1.5rem; }
}

/* ---- motion ---------------------------------------------------------------
 * The glass "gentle effect when you scroll" is a transition, so anyone who has
 * asked their system for less of it gets none.
 * -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .shell *,
  .shell *::before,
  .shell *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
  }
}

/* ---- prose: pages that are read rather than sold --------------------------
 * privacy, the cookie statement, the vacancy and the manifesto. Same glass,
 * same type, same header and footer as everything else — a legal page that
 * looks like a different website reads as one.
 *
 * The office design's scenes exist to hold a photograph. There is none here,
 * so this is a single band with a panel in it, and the work the CSS does is
 * to make a long column of text readable: a narrower measure than the
 * marketing pages use, and space between sections rather than inside them.
 * -------------------------------------------------------------------------- */

.shell .band--prose { padding-block: 3rem; }

/* Narrower than .measure. Legal prose is read line by line rather than
 * scanned, and a 42rem column at this size holds roughly 75 characters —
 * about where a line stops being comfortable to return from. */
.shell .band--prose .panel { max-width: 46rem; margin-inline: auto; }

.shell .prose__title { font-size: 1.875rem; line-height: 1.2; }

.shell .prose__updated {
  margin-top: 0.5rem;
  font-size: 0.875rem;
  color: var(--muted-foreground);
}

.shell .prose__lede {
  margin-top: 1rem;
  font-size: 1.0625rem;
  color: var(--foreground);
}

.shell .prose { margin-top: 2rem; }

.shell .prose__section + .prose__section { margin-top: 2.5rem; }

.shell .prose__heading {
  font-size: 1.125rem;
  font-weight: 700;
  letter-spacing: -0.015em;
}

/* The manifesto has no headings — it is one run of paragraphs — and renders
 * through the same section partial with an empty one rather than needing a
 * second template. An empty <h2> would still take its margin, so it is
 * removed from the flow entirely. */
.shell .prose__heading:empty { display: none; }

.shell .prose__para {
  margin-top: 0.75rem;
  color: var(--muted-foreground);
  line-height: 1.7;
}

.shell .prose__para a { text-decoration: underline; }

.shell .prose__list {
  margin-top: 0.75rem;
  padding-left: 1.25rem;
  list-style: disc;
}

.shell .prose__item {
  margin-top: 0.375rem;
  color: var(--muted-foreground);
  line-height: 1.7;
}

.shell .prose__item a { text-decoration: underline; }

.shell .prose__defs { margin-top: 0.75rem; }

.shell .prose__term {
  margin-top: 0.75rem;
  font-weight: 600;
  font-size: 0.9375rem;
}

.shell .prose__def {
  margin-left: 0;
  color: var(--muted-foreground);
  line-height: 1.7;
}

.shell .prose__def a { text-decoration: underline; }

/* A value the business has not filled in yet. Marked rather than hidden, so
 * an unfinished legal page looks unfinished instead of looking complete. */

/* The cookie table is the one thing here too wide for a phone. It scrolls
 * inside its own box so the page body never does. */
.shell .prose__scroll {
  margin-top: 1rem;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.shell .prose__table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.875rem;
  text-align: left;
}

.shell .prose__row + .prose__row { border-top: 1px solid var(--border); }

.shell .prose__cell {
  padding: 0.625rem 0.75rem;
  vertical-align: top;
  color: var(--muted-foreground);
}

.shell .prose__table thead .prose__cell {
  color: var(--foreground);
  font-weight: 600;
  white-space: nowrap;
}

.shell .prose__signature {
  margin-top: 2.5rem;
  font-weight: 600;
}

@media (min-width: 640px) {
  .shell .band--prose { padding-block: 4rem; }
  .shell .prose__title { font-size: 2.25rem; }
}

/* ---- band variants: the pages with no photograph --------------------------
 * The six niche service pages. The office design opens on a photograph
 * because the photograph is evidence — it shows the room being sold. These
 * pages have no photography of their own, and borrowing /kantoor's put an
 * office desk above the gutter-cleaning page, which reads as stock filler and
 * costs more trust than the space it fills.
 *
 * So the page is carried by structure instead, and the brief for it is
 * restraint: nothing moves, nothing glows, nothing asks to be looked at. One
 * tint step between bands, a hairline where they meet, and type at a measure
 * you can read. "i want to see no glow gradient crap" — DESIGN.md.
 *
 * Note what is deliberately NOT here: no .panel. The glass exists to lift
 * copy off a photograph, and glass over a flat background is a heavy frame
 * around nothing. Without a picture behind it the copy can simply sit on the
 * page.
 * -------------------------------------------------------------------------- */

/* The quiet alternation. --secondary is one step off the page background —
 * enough to separate two sections, not enough to read as a coloured panel. */
.shell .band--tint { background: var(--secondary); }

/* A hairline where two bands meet, so the rhythm survives when two adjacent
 * bands happen to share a background. */
.shell .band + .band { border-top: 1px solid var(--border); }

/* The opening band is filled with the deep teal, and that is doing two jobs.
 *
 * The first is a bug fix. `.hero__title` and `.hero__blurb` are set in
 * --primary-foreground with a text-shadow, because on /kantoor they sit on a
 * photograph over a scrim. Dropping the photograph left near-white type on a
 * near-white page — the headline of the gutter page was legible only as an
 * embossed ghost. Type built for a dark ground needs a dark ground.
 *
 * The second is the space itself. "maybe the entire site should be shifted
 * more towards teal on the bg and not so white — white should be less of an
 * element, in terms of how the site feels" (DESIGN.md). A flat field of the
 * brand's own colour is what fills the room a photograph used to hold open,
 * and it fills it without pretending to be evidence the way a borrowed
 * photograph did.
 *
 * Flat, not a gradient. "i want to see no glow gradient crap." */
.shell .band--open {
  background: var(--teal-deep);
  padding-top: 7rem;
}

/* A band that carries a photograph.
 *
 * .scene__photo is `position: absolute; inset: 0; z-index: -10`, which needs
 * a positioned ancestor and a background it can sit in front of. Without
 * `position: relative` it anchors to the viewport; with the flat teal above
 * it, a z-index of -10 puts it behind the paint. Both were true at once, so
 * the photograph was in the DOM, correctly sized, and invisible.
 *
 * :has() rather than a modifier class, so a page gains the treatment by
 * having a photograph rather than by remembering to say it does. */
.shell .band--open:has(.scene__photo) {
  position: relative;
  background: var(--teal-deep);
  isolation: isolate;
  /* .scene clips for this reason and the band has to as well. A framed photo
     carries `transform: scale()` — that is what zooming in the builder means —
     and a scaled image in a container that does not clip spills sideways. The
     document then becomes wider than the viewport, and the first thing that
     shows it is the fixed nav being cut off at the right on a phone. */
  overflow: hidden;
}

.shell .band--open:has(.scene__photo) .scene__photo {
  z-index: 0;
}

.shell .band--open:has(.scene__photo) > .measure {
  position: relative;
  z-index: 1;
}

/* The type goes back onto a photograph, so it needs lifting off it again —
 * the rule above turned the shadow off for the flat-colour case. */
.shell .band--open:has(.scene__photo) .hero__title,
.shell .band--open:has(.scene__photo) .hero__blurb {
  text-shadow: 0 2px 18px rgb(0 28 31 / 0.55);
}

/* No photograph underneath, so nothing for the type to lift off. A shadow
 * over flat colour is decoration, and it muddies the letterforms at the
 * weight this typeface is set in. */
.shell .band--open .hero__title,
.shell .band--open .hero__blurb {
  text-shadow: none;
}

/* --muted-foreground is a dark grey for copy on a light page. On the teal it
 * has to come from the other end: the same near-white as the headline, held
 * back so the h1 still leads. */
.shell .band--open .lede,
.shell .band--open .hero__blurb {
  color: rgb(255 251 244 / 0.82);
}

.shell .band--open .region {
  color: rgb(255 251 244 / 0.72);
  border-top-color: rgb(255 255 255 / 0.22);
}

/* The seam border exists to separate two bands that share a background. The
 * teal separates itself. */
.shell .band--open + .band { border-top: 0; }

/* The closing band is the last thing before the footer; the extra room below
 * keeps the CTA off the footer's top border. */
.shell .band--close { padding-bottom: 6rem; }

.shell .band__actions {
  margin-top: 1.75rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

/* The VAT line. A footnote to the cards above it rather than a section of its
 * own, which is how it read when it had a band to itself. */
.shell .sub--note { margin-top: 1.5rem; font-size: 0.8125rem; }

@media (min-width: 640px) {
  /* Side by side once there is width for it. Column on a phone, because two
   * full-width buttons stacked are easier to hit than two half-width ones. */
  .shell .band__actions { flex-direction: row; align-items: center; }
}

/* Photo framing, written by tools/page-builder.
   --frame is x y zoom in the photograph's own coordinates and is the value
   that matters; the declarations after it are derived from it and are what
   the browser obeys. Edit by dragging.

   Concatenated into /site.css by sitegen/default.nix, so a crop applies to
   every page a photo appears on rather than to the one page that happened to
   hold a <style> block. */
#photo-professional-cleaning-staff-wiping { --frame: 0.5 0.0821 1; object-position: 50% 8.2%; }
#photo-professional-cleaning-team-ready { --frame: 0.142 0.2936 1.117; object-position: 14.2% 29.4%; transform: translate(4.2%, 2.7%) scale(1.117); }
#photo-thorough-cleaning-of-all { --frame: 0.1243 0.5889 1.288; object-position: 12.4% 58.9%; transform: translate(10.8%, -2.9%) scale(1.288); }
#photo-woman-cleaning-modern-office { --frame: 0.67 0 1; object-position: 67% 0%; }
#photo-woman-cleaning-window-with-spray { --frame: 0.5 0.5 1; object-position: 50% 50%; }
#photo-woman-cleans-window-with-gloves { --frame: 0.5 0.5 1; object-position: 50% 50%; }
#photo-woman-holding-a-bucket { --frame: 0.2121 0 1; object-position: 21.2% 0%; }
#photo-woman-in-green-gloves-oven-door { --frame: 0 0.3625 1.197; object-position: 0% 36.3%; transform: translate(9.9%, 3%) scale(1.197); }
@media (max-width: 640px) {
  #photo-professional-cleaning-staff-wiping { --frame: 0.5021 0 1.15; object-position: 50.2% 0%; transform: translate(-0.1%, 7.5%) scale(1.15); }
  #photo-professional-cleaning-team-ready { --frame: 0.5951 0.5 1; object-position: 59.5% 50%; }
  #photo-thorough-cleaning-of-all { --frame: 0.5884 1 1.272; object-position: 58.8% 100%; transform: translate(-7.2%, -13.6%) scale(1.272); }
  #photo-woman-cleaning-modern-office { --frame: 0.6366 0.0525 1.17; object-position: 63.7% 5.3%; transform: translate(-6.9%, 7.6%) scale(1.17); }
  #photo-woman-holding-a-bucket { --frame: 0.2836 0 1.037; object-position: 28.4% 0%; transform: translate(2.4%, 1.9%) scale(1.037); }
  #photo-woman-in-green-gloves-oven-door { --frame: 0.3489 0.0937 1.272; object-position: 34.9% 9.4%; transform: translate(12.2%, 11.1%) scale(1.272); }
}
