/* A recreation of the app, drawn at its own size.
 *
 * Everything inside `.phone` is laid out in app points as px, exactly the
 * numbers the Swift carries: 390x844 of screen, a 92pt shutter, a 48.67pt
 * island, a 362x482.67 viewfinder standing at 14,134.5, 84.5pt cells, an 11pt
 * lap between two prints in the strip. The whole thing is then scaled down by
 * one transform. Copying the numbers is the point: a demo redrawn "about
 * right" drifts from the app the first time either moves, and here a wrong
 * number is a number that does not match a source file.
 *
 * The app is dark in every light, so its palette is scoped to `.phone` under
 * names of its own. The page's own --ink and --bone flip white in the light
 * half, and the app does not.
 */
.phone{
  --app-ink:#0B0A09;
  --app-surface:#161412;
  --app-raised:#23201D;
  /* Palette.control, the disc under every icon control. A fourth grey,
     and it exists because the ground moved: `raised` at 35 was a visible
     disc while the chrome stood on ink at 11, and the camera's ground is
     `surface` at 22 now so the island can be seen at all. 50 restores the
     separation the old pairing had. */
  --app-control:#322E2A;
  --app-bone:#F2EDE6;
  --app-muted:#8C8379;
  --app-faint:#514B45;
  --app-amber:#F5A343;
  --app-signal:#FFD60A;
  --app-film:#E4553C;
  --paper:#F6F2EC;
  --paper-edge:#D9D3CA;
  --paper-ink:#4C4742;
  /* PrintMetrics.cornerRatio: TinycamArt.apertureRadius over the width that
     aperture actually is, 96/810. Derived in the app and never transcribed,
     because the icon is generated; here it is the one number a print's two
     corners are both cut with. `PrintStyle.round`, which is the app's
     default: the paper's corner is this ratio of the paper's width and the
     photograph's is the same ratio of the photograph's, so a print is the
     shape of the mark's aperture at every size, from a 50pt strip cell to
     the 326pt one in the viewer. */
  --print-corner:.1185185;
  /* What is left of glass: the two controls in the app that are a word or a
     field rather than a glyph, which is `glassPill`. No icon wears it any
     more. Palette.bone at 0.10 is its rim. */
  --glass:rgba(242,237,230,.075);
  --glass-rim:rgba(242,237,230,.16);
  /* TypeScale: a serif for dates, a mono for machine facts, the system's own
     for controls, a hand for what is written on paper. `ui-serif` and
     `ui-monospace` are New York and SF Mono on the phones this app ships to,
     which is what the app is actually set in; the named faces are the fallback
     for everyone else and are already on the page. */
  --app-serif:ui-serif,'Iowan Old Style',Fraunces,Georgia,serif;
  --app-mono:ui-monospace,SFMono-Regular,'Plex Mono',Menlo,monospace;
  --hand:Caveat,'Segoe Script','Bradley Hand',cursive;
}
@font-face{font-family:Caveat;src:url(/fonts/caveat-latin.woff2) format('woff2');font-weight:500;font-display:swap}

/* ---- the section ------------------------------------------------------- */

.tour{
  margin:48px auto 0;
  max-width:700px;
  text-align:center;
  container-type:inline-size;
}
.stage{
  /* The scale is written by demo.js against the space actually available. The
     value here is the one a desktop lands on, so a page with no JavaScript
     still draws the phone at a sane size rather than at 390x844 of a viewport
     that may be 360 wide. */
  --s:.78;
  width:calc(390px * var(--s));
  height:calc(844px * var(--s));
  margin:0 auto;
  position:relative;
}

/* ---- the handset ------------------------------------------------------- */

.phone{
  position:absolute;
  top:0;
  left:0;
  width:390px;
  height:844px;
  transform:scale(var(--s));
  transform-origin:top left;
  border-radius:54px;
  background:#000;
  /* The rim, then the drop. A phone on a page has to sit on something or it
     reads as a rectangle of screenshot. */
  box-shadow:
    0 0 0 2px #26221E,
    0 0 0 3.5px #0d0c0b,
    0 30px 60px rgba(0,0,0,.55),
    0 6px 18px rgba(0,0,0,.4);
}
.screen{
  position:absolute;
  inset:0;
  overflow:hidden;
  border-radius:54px;
  background:var(--app-ink);
  color:var(--app-bone);
  font-family:var(--sans);
  text-align:left;
  isolation:isolate;
}

/* ---- controls ---------------------------------------------------------- */

/* ControlRow: every icon control owns a 48pt target with a 40pt disc inside
   it, and the disc is neutral in every state.

   `controlGround`: one flat fill of Palette.control, no rim and no material.
   It was glass twice and is not any more, because glass earns its rim where
   there is something under it to sample and on this app there mostly is not.
   The camera's chrome stands on the ground beside the frame, the lane's bar on
   its own scrim; a flat disc reads on both because it is opaque. Only the door
   into the gallery draws on its ground, a hard 2pt Palette.signal border. */
.ctl{
  position:absolute;
  width:48px;
  height:48px;
  display:grid;
  place-items:center;
}
.ctl i{
  display:grid;
  place-items:center;
  width:40px;
  height:40px;
  border-radius:50%;
  background:var(--app-control);
  color:var(--app-bone);
}
.ctl svg{width:21px;height:21px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
/* The three glyphs that name the app's own symbols are those symbols, redrawn
   on the 24 grid rather than approximated: `photo.stack` on the door, off the
   numbers Android traced from the symbol at medium (sheets as tops alone, a
   2.08 frame with a 2.53 corner, the sun and hills clipped by its inside
   edge); `arrow.triangle.2.circlepath` on the flip, two 134-degree arcs at
   9.4 with filled heads at three and nine o'clock; `camera.aperture` on the
   lane's way back, six blades tangent to a 3.5 hub, rim to the hexagon they
   make. Each was rendered at 220px beside the symbol before it went in; the
   earlier hand-drawn ones (a card with a corner, two bent arrows, a hexagon
   in a ring) read as three different apps (2026-09-10). Filled parts are
   `.f`; the two stroked glyphs state the weight SF draws them at. */
.ctl svg .f{fill:currentColor;stroke:none}
.cam-gallery svg rect{stroke-width:2.08}
.cam-flip svg{stroke-width:2.1}
.lane-camera svg{stroke-width:1.9}
/* The zoom is a word rather than a glyph, in the mono the app sets every
   machine fact in. `font-style` is stated because the disc is an `<i>` and a
   word inside one comes out oblique, which is not what TypeScale.meta is. */
.ctl b{font-family:var(--app-mono);font-size:14px;font-weight:600;font-style:normal;line-height:1}
.ctl.door i{border:2px solid var(--app-signal);color:var(--app-signal)}
.ctl.quiet i{color:var(--app-muted)}
/* A control answering a finger. The app does this with a press state on the
   ground and nowhere else. */
.ctl.is-pressed i{transform:scale(.94);background:rgba(242,237,230,.14)}
.ctl i{transition:transform .12s ease-out,background-color .12s ease-out}

/* ---- the camera -------------------------------------------------------- */

/* The camera's ground is Palette.surface, not ink, and that is what makes the
   island visible at all: the pill is pure black and the only thing that has
   ever shown it is something lighter behind it. On ink it would be 11/255
   against the pill's 0 and the slot would open and shut without a mouth ever
   appearing. */
.cam{position:absolute;inset:0;z-index:1;background:var(--app-surface)}

/* The viewfinder is a frame, not the screen. `Viewfinder.frame` on a 390x844
   display with a 59pt top inset: 362 wide by 482.67 at 3:4, standing at 14,
   134.5 — centred between the two things you can see, the island's drawn lower
   edge (53.67) and the line the flash and the zoom stand on (698 from the top),
   never on the display and never on either invisible boundary near them.

   Its corner is the print's corner and the mark's: PrintMetrics.cornerRatio,
   96/810 of the width, which is 42.9 here. The same shape at every size is
   what makes the capture's morph free in the app. */
.cam .vf-frame{
  position:absolute;
  left:14px;
  top:134.5px;
  width:362px;
  height:482.67px;
  border-radius:42.9px;
  overflow:hidden;
  /* The ground behind the still while it decodes. `.vf` is `loading="lazy"`
     and `decoding="async"`, so the first frame after a load can be this black
     and nothing else: a screenshot taken there shows an empty frame while the
     DOM reports the image loaded, laid out and opaque. That is the capture
     racing the decode, not a layout or a clip. Take a second one. */
  background:#000;
}
.cam .vf{
  position:absolute;
  inset:0;
  width:100%;
  height:100%;
  object-fit:cover;
  object-position:50% 38%;
  /* The viewfinder is a still, so it is given the only thing a still can have:
     a drift. Nothing in the app does this; it is the demo admitting the camera
     is not running. */
  animation:vf-drift 26s var(--develop) infinite alternate;
}
@keyframes vf-drift{from{transform:scale(1.045) translate(0,0)}to{transform:scale(1.075) translate(-1.2%,-1.4%)}}
.cam-grain{
  position:absolute;
  inset:0;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
  opacity:.05;
  mix-blend-mode:overlay;
}

/* Four corners with the shutter in the middle, and all five are down at the
   foot of the screen: what the next frame will look like on the upper line,
   where the shots go on the lower one. The camera draws nothing at the top
   except the island.

   `ControlRow` from the foot of the display up. The lower line's centre is
   `centre` = bottomGap 20 + shutter/2 46 = 66, so a 48pt target sits 42 off
   the bottom. The upper line stands exactly one disc away, `stack` = ring * 2
   = 80, which puts `settingsCentre` on 146 and its targets 122 off the bottom:
   a disc's width of ground between one disc and the next, the same law the
   shutter is built on. All four are on `sideInset` 26.

   They are two lines, not two stacked rows, and that is what let them close
   up: stacked, the upper row sat above the whole of the shutter's row while
   the flip occupied only 48pt centred in it, so 18pt of nothing was
   structural. Said as a distance between two lines the upper pair simply
   descends into that dead band, which is empty either side of the shutter,
   exactly where the flash and the zoom are. */
.cam-flash{left:26px;bottom:122px}
.cam-zoom{right:26px;bottom:122px}
.cam-gallery{left:26px;bottom:42px}
.cam-flip{right:26px;bottom:42px}

/* A disc standing in a ring, not a disc with an outline: 92 across, a 7pt
   muted track, and the 78pt disc fills it with nothing between them. The track
   was bone at 0.85, which is within a few points of the disc it surrounds, so
   the two read as one blob with a seam in it.

   It is lifted rather than laid out into the middle of the four corners:
   `shutterLift` is half of `stack`, so its centre is on 106 and its own row
   keeps the height the frame above is measured against. */
.shutter{
  position:absolute;
  left:50%;
  bottom:60px;
  width:92px;
  height:92px;
  margin-left:-46px;
  border-radius:50%;
  /* The track is a disc showing from behind, not a border. A 7pt border and a
     78pt disc are exactly tangent, so the two antialiased edges land on the
     same pixel and leave a dark hairline all the way round: the seam the ring
     being muted rather than bone exists to prevent, drawn back in by the
     renderer. Filled, the annulus is what is left over and there is no edge
     between them at all. */
  background:var(--app-muted);
  display:grid;
  place-items:center;
}
.shutter::after{
  content:"";
  width:78px;
  height:78px;
  border-radius:50%;
  background:var(--app-bone);
  transition:transform .12s ease-out;
}
.shutter.is-pressed::after{transform:scale(.86)}

/* ---- the first shot's mark ---------------------------------------------- */

/* FirstCaptureMark: the one thing the app ever teaches about itself, said
   once, over the shot that has just landed. Three things at once, none of
   which works alone: what the press cost against the stock camera, that it
   stayed on the phone, and where it went, by pointing at the corner it went
   to. It is modal because two quieter versions were verified to render for six
   seconds and not be read.

   The scrim is `SpotlightScrim`: Palette.ink at 0.78 over the whole display
   with one hole the size of the door's own 48pt target, so the real door stays
   lit inside it. The hole is on the point the camera and the lane share,
   `ControlRow.cornerCentre` 50 across and `ControlRow.centre` 66 up, which is
   778 down a 844pt screen. The tour has no morph to freeze and no way out to
   press, so it goes out the way the app's second exit does: the tap on the
   door that opens the lane. */
.mark{
  position:absolute;
  z-index:2;
  inset:0;
  opacity:0;
  pointer-events:none;
  transition:opacity .3s var(--develop);
  background:radial-gradient(circle at 50px 778px,transparent 23.5px,rgba(11,10,9,.78) 24.5px);
}
.phone.is-marked .mark{opacity:1}
/* Leading-aligned on the line the door's glass starts on, `sideInset` 26 +
   `ringInset` 4, not on the row's touch inset: a statement about a corner is
   set against the thing you can see. Its foot clears the hole by 6. */
.mark-copy{
  position:absolute;
  left:30px;
  right:30px;
  bottom:96px;
  display:flex;
  flex-direction:column;
  align-items:flex-start;
  gap:10px;
}
.mark-head,.mark-body,.mark-here,.mark-pointer{filter:drop-shadow(0 1px 5px rgba(0,0,0,.6))}
.mark-head{font-family:var(--app-serif);font-size:19px;line-height:23px;color:var(--app-bone)}
/* Three lines at 300, counted in the app with a framesetter rather than
   guessed off a screenshot: it was four, which is one more than anything else
   in the app asks anybody to read. */
.mark-body{max-width:300px;font-size:13px;line-height:17px;color:var(--app-muted)}
.mark-here{font-size:15px;font-weight:500;line-height:18px;color:var(--app-bone)}
/* Long enough to cross the whole settings row, which is what makes it a
   pointer rather than a tick: from 6 above the hole to 16 above
   `Viewfinder.chromeHeight`, 90 in all. Its own width is stated, and it is
   set `cornerInset` 50 less the copy's 30 less half of itself in, so the line
   drops on the door's centre exactly. Palette.signal by the rule the door
   wears it. */
.mark-pointer{display:block;margin-left:14px;width:12px;height:90px;fill:none;stroke:var(--app-signal);stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
.mark-pointer path+path{stroke-width:1.8}
/* The way out, in `glassPill`, and it stands on the shutter as it does in
   the app: centred on the display, its centre on the lifted shutter's own
   centre, `ControlRow.centre` 66 + `shutterLift` 40 = 106 up, so a 34pt pill
   has its foot on 89. The shutter is dimmed and dead under the scrim, so the
   pill is what the thumb finds where it went to shoot. It shared the
   headline's row before, which is not where the app puts it. */
.mark-ok{
  position:absolute;
  left:50%;
  bottom:89px;
  transform:translateX(-50%);
  display:grid;
  place-items:center;
  height:34px;
  padding:0 18px;
  border-radius:17px;
  background:var(--glass);
  border:1px solid var(--glass-rim);
  font-size:15px;
  font-weight:500;
  line-height:1;
  color:var(--app-bone);
  white-space:nowrap;
}

/* The lane covers the camera and the camera is dimmed behind it, never scaled
   and never offset. */
.dim{position:absolute;inset:0;z-index:2;background:var(--app-ink);opacity:0;transition:opacity .5s var(--develop);pointer-events:none}
.phone[data-scene="lane"] .dim,
.phone[data-scene="viewer"] .dim{opacity:.62}

/* ---- the island -------------------------------------------------------- */

/* A foreground app cannot draw in the real Dynamic Island; this is a replica
   aligned to the cutout, pure black, and it is drawn last so it occludes the
   print being filed. IslandMetrics: a 125.33 x 36.67 cutout, overhung by 6 on
   every side, its lower edge 11.33 above the bottom of the top safe area. */
.island{
  position:absolute;
  z-index:6;
  top:5px;
  left:50%;
  width:137.33px;
  height:48.67px;
  margin-left:-68.66px;
  border-radius:24.33px;
  background:#000;
  overflow:hidden;
  transition:height .42s var(--develop),width .42s var(--develop),border-radius .42s var(--develop);
}
/* It widens and deepens only for the print slot: 116pt so the shelf clears the
   cutout. At rest it says nothing at all. */
.phone.is-filing .island{height:116px;width:186px;margin-left:-93px;border-radius:34px}
.fan{
  position:absolute;
  left:0;
  right:0;
  bottom:10px;
  height:62px;
  display:flex;
  justify-content:center;
  align-items:flex-end;
  gap:0;
  opacity:0;
  transition:opacity .3s var(--develop) .12s;
}
.phone.is-filing .fan{opacity:1}
.fan .pr{margin:0 -6px}

/* ---- a print ----------------------------------------------------------- */

/* PolaroidPrint: one side margin and one lower margin, both ratios of the
   width, a square image between them, and a half-point edge. A gallery cell
   leaves the margin blank and still reserves its full height.

   **Two corners, and `PrintStyle.round` cuts both.** The paper's is the ratio
   of the paper's own width, the photograph's is the same ratio of the
   photograph's, which is why they are stated separately rather than one
   inherited from the other: they are parallel, not equal. It was `classic`
   here, 2px of paper relief around a square-cut picture, which is what the app
   looked like before the mark's corner became the rule and is not what it
   ships as. */
.pr{
  --m:calc(var(--pw) * .035);
  --lm:calc(var(--pw) * .115);
  position:relative;
  width:var(--pw);
  padding:var(--m) var(--m) 0;
  margin:0;
  background:var(--paper);
  border-radius:calc(var(--pw) * var(--print-corner));
  box-shadow:inset 0 0 0 .5px rgba(217,211,202,.7);
}
.pr-img{
  display:block;
  width:100%;
  aspect-ratio:1;
  overflow:hidden;
  background:var(--app-surface);
  border-radius:calc((var(--pw) - var(--m) * 2) * var(--print-corner));
}
.pr-img img{width:100%;height:100%;object-fit:cover;display:block}
/* **The hand begins and ends where the paper's corner stops turning**, not at
   the margin, so this inset is a function of the corner and moved when the
   corner did. `PrintMetrics.writingInset` is the card's corner measured from
   the paper's edge, and this box is already the photograph's width, so what is
   left to pay is that corner less the margin — the app's own
   `max(max(cardCorner, margin) - margin, 0)`, which came to nothing under
   `classic` (a 2px corner is inside the margin) and comes to 27px on the
   viewer's print under `round`. Left flush, the writing sat inside the curve.

   The mark is the reference for it: its caption bar starts at 0.1992 of the
   card's side against a card radius of 0.2188, so the stroke begins where the
   corner has finished. */
.pr-note{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:6px;
  height:var(--lm);
  padding:0 calc(max(var(--pw) * var(--print-corner), var(--m)) - var(--m));
  font-family:var(--hand);
  font-size:calc(var(--pw) * .055);
  line-height:1;
  color:rgba(76,71,66,.82);
  white-space:nowrap;
  overflow:hidden;
}
.pr-note span{overflow:hidden;text-overflow:clip}
/* A clip is marked in the picture's trailing corner in the grid. */
.pr-clip{
  position:absolute;
  right:calc(var(--m) + 3px);
  top:calc(var(--m) + 3px);
  display:flex;
  align-items:center;
  gap:2px;
  padding:1px 4px;
  border-radius:6px;
  background:rgba(11,10,9,.55);
  font-family:var(--app-mono);
  font-size:8px;
  font-weight:600;
  color:var(--app-bone);
}

/* ---- the print in flight ----------------------------------------------- */

/* A press sends a print to the island and disturbs nothing else. It used to
   freeze the viewfinder and morph the whole screen into the print, the way the
   app's own `PolaroidEjectLayer` does, and on a page it read as a fault: the
   viewfinder here is a still with a slow drift on it, so the frozen copy
   started at the drift's scale and the picture jumped the instant the shutter
   was pressed. The app has a live frame to freeze and this does not, so the
   print simply arrives.

   Left as one element the whole way: its geometry, its paper and its opacity
   are written by the two animations in demo.js, so nothing here says where it
   is, only what it is made of. */
.flight{
  position:absolute;
  z-index:5;
  left:0;
  top:0;
  opacity:0;
  pointer-events:none;
  overflow:hidden;
  background:var(--paper);
  /* The same print, so the same two corners and the same margins, off the same
     `--pw`. demo.js sets that one property and nothing else about the paper:
     the ratios used to be spelled out a second time in a `pad()` helper there,
     which is a print's proportions written twice and two places to miss when
     one moves. */
  --m:calc(var(--pw) * .035);
  --lm:calc(var(--pw) * .115);
  padding:var(--m) var(--m) var(--lm);
  border-radius:calc(var(--pw) * var(--print-corner));
  /* Scaled about its own corner, because that is the only origin whose
     arithmetic is the rect it is travelling to. */
  transform-origin:0 0;
  will-change:transform;
}
/* A print held over the viewfinder needs an edge; one landing on the page the
   viewer draws must not have one, or it pops as it hands over. */
.flight.lifted{box-shadow:0 8px 20px rgba(0,0,0,.5)}
.flight img{
  width:100%;
  height:100%;
  object-fit:cover;
  display:block;
  border-radius:calc((var(--pw) - var(--m) * 2) * var(--print-corner));
}

/* ---- memory lane ------------------------------------------------------- */

/* One surface rather than a second screen: the sheet rises over the camera and
   the camera never unloads behind it. */
.lane{
  position:absolute;
  z-index:3;
  inset:0;
  background:var(--app-ink);
  border-radius:38px 38px 0 0;
  transform:translateY(100%);
  transition:transform .52s var(--develop);
  overflow:hidden;
}
.phone[data-scene="lane"] .lane,
.phone[data-scene="viewer"] .lane{transform:none}
/* The grid gives way while the print grows out of it, so by the time the print
   is the page there is nothing behind it to cut away from. The sheet's own ink
   stays: what fades is everything drawn on it, which is why this is on the
   children and not on the lane. */
.lane > *{transition:opacity .38s var(--develop)}
.phone.is-opening .lane > *{opacity:0}
/* The band above the header is black, drawn over the timeline, which fills
   past the top of its own frame. */
.lane-band{position:absolute;top:0;left:0;right:0;height:59px;background:#000;z-index:2}
.lane-head{
  position:absolute;
  top:59px;
  left:0;
  right:0;
  height:48px;
  z-index:2;
  display:flex;
  align-items:flex-start;
  gap:4px;
  padding:0 12px 0 20px;
  background:var(--app-ink);
}
.lane-title{font-family:var(--app-serif);font-size:20px;line-height:26px;color:var(--app-bone)}
.readout{
  margin-left:auto;
  margin-right:8px;
  height:32px;
  display:flex;
  align-items:center;
  gap:6px;
  padding:0 10px;
  border-radius:16px;
  background:var(--glass);
  border:1px solid var(--glass-rim);
  font-family:var(--app-mono);
  font-size:11px;
}
.readout b{font-weight:600;color:var(--app-bone)}
.readout s{color:var(--app-faint);text-decoration-thickness:.5px}

.lane-scroll{position:absolute;inset:107px 0 0;overflow:hidden}
/* The grid keeps exactly the rail's width clear, `YearScrubber.railWidth`,
   the way the app pads the list's trailing edge: the rail is part of what the
   grid is, not a control laid over it. */
.lane-inner{padding:0 30px 133px 0;transition:transform 3.4s linear}

/* The year rail: a film edge down the right of the list, one tick per month
   and the year written where a year begins, spread over the list's height with
   24 clear at either end. It stands still while the grid scrolls under it,
   which is why it is a sibling of the scrolling inner and not inside it. */
.year-rail{position:absolute;top:24px;right:0;bottom:24px;width:30px;pointer-events:none}
.year-rail i,.year-rail b{position:absolute;right:12px}
.year-rail i{width:8px;height:1px;background:rgba(242,237,230,.14)}
.year-rail b{
  font-family:var(--app-mono);
  font-size:9px;
  font-weight:600;
  line-height:1;
  white-space:nowrap;
  color:var(--app-faint);
}

/* A month is a folder. The card is one shape behind the whole month, with the
   tab cut into its top edge, and the prints are laid over it: a card attached
   to a cell would land on its left-hand neighbour's print. */
.month{position:relative;padding-bottom:20px}
.month-card{position:absolute;inset:0;bottom:20px;z-index:0}
.month-card svg{width:100%;height:100%;display:block}
.month-card path{
  fill:rgba(242,237,230,.055);
  stroke:rgba(242,237,230,.10);
  stroke-width:1;
}
.month-body{position:relative;z-index:1}
.month-tab{
  height:27px;
  padding:2px 0 0 31px;
  font-family:var(--app-serif);
  font-size:13px;
  line-height:20px;
  color:var(--app-bone);
}
.month-count{
  height:24px;
  padding:0 20px 0 0;
  text-align:right;
  font-family:var(--app-mono);
  font-size:10px;
  font-weight:500;
  letter-spacing:.06em;
  color:var(--app-faint);
}

/* A day is a date line and then loose rows of prints. Nothing wraps a day's
   prints: the day number is a column of its own so the weekday and the place
   start at the same x on every row. */
.day-head{display:flex;align-items:center;gap:6px;padding:0 20px 12px}
/* Two digits of its own face wide, which with tabular figures is what `ch`
   measures, so the column is as wide as the 31st and not a point more. */
.day-num{
  width:2ch;
  font-family:var(--app-serif);
  font-weight:300;
  font-size:30px;
  line-height:1;
  font-variant-numeric:tabular-nums;
  color:var(--app-bone);
}
.day-lines{display:flex;flex-direction:column;gap:2px}
.day-wd{font-family:var(--app-mono);font-size:10px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--app-faint)}
.day-place{font-family:var(--app-mono);font-size:10px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--app-muted)}
/* A grid rather than a wrapping flex row. Four cells of 84.5 and three gaps of
   4 come to exactly the 350 available, and at a fractional page scale that
   exactness rounds the other way and drops the last print onto a line of its
   own. The track does the arithmetic instead; `--pw` stays the cell's width,
   because the paper's margins are ratios of it. */
.day-rows{display:grid;grid-template-columns:repeat(var(--cols,4),minmax(0,1fr));gap:4px;padding:0 20px}
.day-rows .pr{width:auto}
.day+.day{margin-top:30px}
.month .day:last-child .day-rows{padding-bottom:12px}

/* The scrim is not the bar, and that is why the grid stops being legible
   before it reaches the foot of the screen rather than at the bar's own edge.
   It runs the full `height + bottomPad + 56` to the bottom of the display,
   solid ink for the last half of it: hung on the bar instead it stopped 34pt
   short, and a row of prints ran out from under it to the bottom edge. */
.lane-scrim{
  position:absolute;
  z-index:3;
  left:0;
  right:0;
  bottom:0;
  height:154px;
  pointer-events:none;
  background:linear-gradient(to bottom,
    rgba(11,10,9,0) 0%,
    rgba(11,10,9,.86) 26%,
    var(--app-ink) 44%);
}

/* The bar is one bar in three states at one height and one place. Its way back
   to the camera is drawn on ControlRow.centre, the very point the camera draws
   the way in on, so the row is placed rather than centred inside the scrim: 64
   of bar at `bottomPad` 34 off the foot puts its middle on 66. */
.lane-bar{
  position:absolute;
  z-index:4;
  left:0;
  right:0;
  bottom:34px;
  height:64px;
  display:flex;
  align-items:center;
  padding:0 26px;
}
.lane-bar .ctl{position:static}
.lane-bar .spacer{flex:1}

/* ---- the viewer -------------------------------------------------------- */

.viewer{
  position:absolute;
  z-index:4;
  inset:0;
  background:var(--app-ink);
  opacity:0;
  pointer-events:none;
  transition:opacity .28s var(--develop);
}
.phone[data-scene="viewer"] .viewer{opacity:1}
/* The way back on the leading edge, and on the trailing one the hold menu's
   three in the app's order read from the edge: share on the corner, save
   beside it, the trash a step off both. `ViewerScreen.chrome` spaces them 8. */
.viewer-chrome{position:absolute;top:59px;left:0;right:0;height:48px;display:flex;align-items:center;gap:8px;padding:0 14px;z-index:2}
.viewer-chrome .ctl{position:static}
.viewer-chrome .spacer{flex:1}
.viewer-print{position:absolute;top:418px;left:50%;transform:translate(-50%,-50%)}
.viewer-print .pr{--pw:326px}

/* The strip is one folder per month, and the middle of it is the mark. */
.strip{position:absolute;left:0;right:0;bottom:24px;height:92px;overflow:hidden}
.strip-rail{position:absolute;left:0;top:8px;height:84px;display:flex;align-items:flex-end;will-change:transform;transition:transform .55s var(--develop)}
.strip-folder{position:relative;height:84px;flex:none}
.strip-folder+.strip-folder{margin-left:12px}
.strip-card{position:absolute;inset:0;z-index:0}
.strip-card svg{width:100%;height:100%;display:block}
.strip-card path{fill:rgba(242,237,230,.055);stroke:rgba(242,237,230,.10);stroke-width:1}
.strip-tab{
  position:absolute;
  top:0;
  left:0;
  height:19px;
  padding-left:10px;
  z-index:2;
  font-family:var(--app-serif);
  font-size:11px;
  line-height:17px;
  color:var(--app-bone);
}
.strip-prints{position:absolute;left:7px;top:26px;display:flex;z-index:1}
/* Prints overlap at the lap with no gap inside a folder, and are dimmed with a
   multiply rather than an opacity: at half opacity a pile shows through
   itself. Only the one being held casts a shadow. */
.strip-prints .pr{--pw:50px;flex:none;filter:brightness(.62) saturate(.9);transition:filter .3s ease-out,transform .3s var(--develop)}
.strip-prints .pr+.pr{margin-left:-11px}
.strip-prints .pr.on{filter:none;transform:translateY(-6px);box-shadow:inset 0 0 0 .5px rgba(217,211,202,.7),0 3px 7px rgba(0,0,0,.5)}
/* ---- the finger -------------------------------------------------------- */

.tap{
  position:absolute;
  z-index:7;
  width:44px;
  height:44px;
  margin:-22px 0 0 -22px;
  border-radius:50%;
  background:rgba(242,237,230,.28);
  box-shadow:0 0 0 1px rgba(242,237,230,.35);
  opacity:0;
  pointer-events:none;
}
.tap.is-down{animation:tap .5s var(--develop)}
@keyframes tap{
  0%{opacity:0;transform:scale(1.5)}
  35%{opacity:1;transform:scale(1)}
  100%{opacity:0;transform:scale(.82)}
}

/* ---- the chapters ------------------------------------------------------ */

.chapters{
  display:flex;
  flex-wrap:wrap;
  justify-content:center;
  gap:8px;
  margin:22px 0 0;
  padding:0;
  list-style:none;
}
/* Set like the store buttons rather than like the creed: these are words you
   press, and the page's small uppercase mono is for the lines you only read.
   A tag in caps at this size also has to be tracked open to stay legible,
   which is what made four of them a rail wider than the phone above them. */
.chapters button{
  position:relative;
  overflow:hidden;
  margin:0;
  padding:7px 14px 8px;
  border:1px solid var(--edge);
  border-radius:14px;
  background:var(--pane);
  font-family:var(--sans);
  font-size:13px;
  font-weight:500;
  letter-spacing:-.004em;
  color:var(--muted);
  cursor:pointer;
  transition:color .3s var(--develop),border-color .3s var(--develop);
}
.chapters button:hover{color:var(--bone)}
.chapters button:focus-visible{outline:2px solid var(--signal);outline-offset:2px}
.chapters button[aria-current="true"]{color:var(--bone);border-color:var(--edge-lit)}
/* What is left of the chapter, drawn as the ground under its own label. */
.chapters button::before{
  content:"";
  position:absolute;
  inset:0;
  transform-origin:left;
  transform:scaleX(0);
  background:rgba(242,237,230,.07);
}
.chapters button[aria-current="true"]::before{animation:chapter-run var(--run,6s) linear forwards}
@keyframes chapter-run{from{transform:scaleX(0)}to{transform:scaleX(1)}}
.chapters span{position:relative}

/* The page is light only (2026-10-03): this block was the light half under
   prefers-color-scheme and now always applies. */
.chapters button::before{background:rgba(20,17,15,.06)}

@media (max-width:470px){
  .tour{margin-top:72px}
  .chapters{gap:6px}
  .chapters button{padding:6px 11px 7px;font-size:12.5px}
}
/* A still viewfinder that drifts for ever is a repaint for ever, so it holds
   still while the section is off screen. The tour itself is stopped by the
   same observer; this is the half CSS was doing on its own. */
.tour:not(.is-live) .cam .vf{animation-play-state:paused}

/* No JavaScript and no motion: the phone still stands there with the camera on
   it, which is the one scene that is true of the app at rest. */
@media (prefers-reduced-motion:reduce){
  .cam .vf{animation:none}
  .lane,.viewer,.island,.dim,.mark,.flight,.strip-rail,.lane-inner{transition:none}
  .chapters button[aria-current="true"]::before{animation:none;transform:scaleX(1)}
}
