/* ============================================================
   Bloomi's page on jobi.life.

   WHY THIS IS A SEPARATE FILE

   Bloomi is a second product from the same people. It lives on this
   site because the domain is the one that exists, not because it
   belongs to Jobi: it is free forever, has no account and makes no
   network requests at all, so it shares none of Jobi's funnel.

   If Bloomi ever earns a domain of its own, the move should be a copy
   of four files and not a rewrite. So bloomi.html loads styles.css for
   the shell that both products share (tokens, type, nav, buttons,
   footer, the reveal and rail behaviour) and this file for everything
   that is Bloomi's alone. Nothing in here is referenced by index.html
   or by any Jobi page, and nothing in styles.css is Bloomi-specific
   except section 21, which predates this file and dresses
   bloomi-privacy.html, and section 22, which is the cross-sell band on
   Jobi's own homepage.

   The accent is NOT overridden. Bloomi's design system is Jobi's,
   carried over whole (habits repo, DESIGN.md), and the coral is the
   same coral in both. What separates the two pages is the ground:
   Bloomi opens on the mint, which is the colour bloomi-privacy.html
   already claimed for it on 2026-09-14, and every other page on this
   site opens on the blush.
   ============================================================ */

/* ---------- 1. The page's own ground ---------- */
/* The first section's data-bg sets --ground once script.js runs, but a
   page that paints blush for one frame and then jumps to mint is a
   flash of the wrong product. This is the same hex as --mint. */
body.bloomi-page {
  --ground: #d3e3d6;
}

/* ---------- 2. The nav, in Bloomi's context ----------
   jobi.life's nav carries the Jobi wordmark as its home link, and on
   this page that is still true: this IS the Jobi site and a visitor who
   clicks the mark should land on Jobi's page, which is what it says.
   What changes is that the page announces itself. The mark plus the
   name sits where index.html's wordmark sits, and the CTA on the right
   sells the product you are actually reading about.

   The name is set in the site's display face rather than drawn as a
   lockup. Bloomi has no wordmark yet; type is the honest placeholder,
   and swapping it for real art later is one element. */
/* The wordmark alone. The app icon sat here beside it for a few hours and
   the owner took it off both products (2026-09-17): the wordmark already
   says which page this is, and a tile in front of it only competes with the
   nav pill. The small marks INSIDE that pill stay, because those are links
   to the other product and need to be recognisable at 17px. */
.bl-brand {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  color: var(--ink);
}
/* Height is what is set, not width: the path's box is 296.5 x 71.3, so the
   aspect carries the width and the mark can never stretch. 25px is the size
   it read at beside the icon, and it still sits right without one. */
.bl-brand__type {
  display: block;
  height: 25px;
  width: auto;
  fill: currentColor;
}
/* The line back to Jobi, in the nav's own pill. It is the only item in
   there that leaves this product, so it is set apart rather than sitting
   in the run of Bloomi's own links.

   It carries Jobi's APP ICON, not the bare mark (owner, 2026-09-17), so
   that the two pages mirror each other: index.html's nav shows Bloomi as
   a rounded app tile, and this shows Jobi the same way. Same 17px and the
   same 5px radius as .jb-navbl, and no opacity: the icon is a solid tile
   and dimming it only muddies the square. */
.bl-navback {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}
.bl-navback img {
  width: 17px;
  height: 17px;
  border-radius: 5px;
  display: block;
}

/* ---------- 3. The hero's frame ----------
   The film is 16:9 and carries its own moss ground, exactly as Jobi's
   does, so .stage__film's ratio is already right and only the colour
   behind it is set here: that is what shows for the instant before the
   first frame paints, and it should not be a flash of white. The ground
   is INSIDE the film because the shot pushes past it on the ticks, and
   any crop here would eat real picture. */
.bl-still {
  background: #2f4a38;
}

/* ---------- 4. The music card ----------
   Three of the rail's cards are finished 428x600 artwork and carry their
   own headline inside the SVG. The fourth, music, has no artwork yet, so
   it is built to sit beside them without looking like a different rail:
   one colour block from the same palette, the headline at the top in the
   display face, and the panel capture bleeding off the bottom edge with
   the same 18px corner the artwork uses.

   The capture is DARK on purpose. Every designed card shows a dark panel,
   and a light one here would be the only light screen in the run. Retake
   it from the habits repo with theme=dark, never theme=light.

   When artwork for music arrives, this card becomes a .card--art like the
   other three and this whole section goes. */
.card--bl-shot {
  background: var(--c-peach);
  padding: 44px 32px 0;
  overflow: hidden;
}
.card--bl-shot h3 {
  margin: 0 0 32px;
  font-family: var(--font-display);
  font-size: 30px;
  line-height: 1.2;
  letter-spacing: -0.025em;
  font-weight: 500;
  text-align: center;
  color: var(--ink);
}
/* The panel runs off the bottom of the card rather than stopping inside
   it, which is what the artwork does and what keeps the app reading as
   something that continues past the frame. */
.card--bl-shot .bl-shot__panel {
  flex: 1 1 auto;
  position: relative;
  overflow: hidden;
  border-radius: 18px 18px 0 0;
  background: #1c1616;
}
.card--bl-shot .bl-shot__panel img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: auto;
  display: block;
}

/* Cards the rail colours rather than photographs. */
/* Ink, not cream: the tick artwork already opens the rail on the cream,
   and two cream blocks in one run read as a repeat. */
.card--bl-note {
  background: var(--c-ink);
  justify-content: space-between;
}
.card--bl-note h3 {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(27px, 3vw, 34px);
  line-height: 1.1;
  letter-spacing: -0.03em;
  font-weight: 500;
  color: #ffffff;
}
.card--bl-note p {
  margin: 18px 0 0;
  font-size: 16.5px;
  line-height: 1.5;
  /* On the ink block. Measured 7.1:1. */
  color: #c9c3cd;
}
.card--bl-note .bl-note__art {
  margin-top: auto;
  align-self: center;
  width: 78%;
  max-width: 250px;
  aspect-ratio: 1 / 1;
  position: relative;
}
.card--bl-note .bl-note__art object {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* ---------- 5. The free band ----------
   Where Jobi's page has pricing, Bloomi's has this. It is the strongest
   single thing the product can say and the exact opposite of the page
   one link away, so it is stated plainly and once, at the scale the
   pricing section would have taken. */
.bl-free {
  padding: clamp(80px, 11vw, 160px) var(--gutter) clamp(20px, 3vw, 40px);
}
.bl-free__card {
  max-width: 1180px;
  margin: 0 auto;
  background: var(--c-ink);
  color: #f6f3ef;
  border-radius: var(--radius-card);
  padding: clamp(38px, 5.5vw, 76px) clamp(28px, 5vw, 76px);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(28px, 5vw, 72px);
}
.bl-free__say {
  flex: 1 1 320px;
  min-width: 0;
}
.bl-free__title {
  font-family: var(--font-display);
  font-size: clamp(40px, 6.4vw, 88px);
  line-height: 0.95;
  letter-spacing: -0.03em;
  margin: 0;
  color: #ffffff;
}
.bl-free__title span {
  color: var(--accent);
}
.bl-free__body {
  margin: clamp(18px, 2.4vw, 28px) 0 0;
  font-size: clamp(16px, 1.3vw, 19px);
  line-height: 1.55;
  /* On the ink block. #b8b2bd would be 5.4:1 here; this is the same
     family and clears it with room. */
  color: #c9c3cd;
  max-width: 44ch;
}
.bl-free__list {
  flex: 0 1 300px;
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 14px;
}
.bl-free__list li {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  font-size: 16px;
  line-height: 1.4;
  color: #ece8e4;
}
/* A rule, not a tick: the list is four plain facts and a row of ticks
   would read as a feature comparison. */
.bl-free__list li::before {
  content: '';
  flex: 0 0 auto;
  width: 14px;
  height: 2px;
  margin-top: 10px;
  border-radius: 2px;
  background: var(--accent);
}

/* ---------- 7. Narrow ---------- */
@media (max-width: 900px) {
  .bl-free__card {
    flex-direction: column;
    align-items: flex-start;
  }
  .bl-free__list {
    flex: 1 1 auto;
    width: 100%;
  }
}
@media (max-width: 620px) {
  .bl-brand__type {
    height: 21px;
  }
  .card--shot h3 {
    font-size: 22px;
  }
}

/* ---------- 10. The assembly: one square, one month, one panel ----------
   index.html's .asm is the blueprint and this page reuses it wholesale:
   .asm__stage, .asm__cap, .asm__line, .asm__bar and the whole .asm__window
   browser shell are styles.css's, unchanged, because none of them say
   "Jobi". What is Bloomi's is the thing inside the panel, and that is all
   that is written here.

   THE PANEL IS THE APP'S OWN TODAY SCREEN, in low fidelity, measured off
   the real one at 400px: a title with the add and menu buttons, the scene
   card carrying a music chip and the focus ring, the Habits/Tasks control,
   then four habit rows, each an icon tile, a name, a streak and a tulip,
   with the third one not done yet. The empty run under the fourth row is
   in the real screen too.

   The month is NOT part of that screen. It lives in a habit's streak
   sheet, so it is drawn as a sheet sitting over Today, and at the end of
   the timeline the sheet slides away and leaves Today standing, which is
   what dismissing it actually does.

   The panel is laid out at its RESTING size and never re-laid-out. Every
   move in the timeline is a transform, which is why the matrix can be
   six hundred pixels wide at the start and forty at the end without a
   single reflow. The one exception is the dock at the very end, where the
   width really does change, and the grid is seven fractional columns so it
   takes that without complaint. */
.bl-asm .asm__stage {
  position: relative;
}
.bl-asm__tiles {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.bl-asm__tiles > * {
  position: absolute;
  left: 50%;
  top: 50%;
}

.bl-asm__panel {
  /* Above .asm__window, which styles.css puts on z-index 1. The panel docks
     INSIDE the browser, so without this the white page paints over it and
     the last shot of the section is an empty window. */
  z-index: 2;
  width: 380px;
  height: 620px;
  box-sizing: border-box;
  overflow: hidden;
  border-radius: 22px;
  /* The app's own canvas, measured: rgb(22, 15, 15). */
  background: #160f0f;
}
.bl-asm__today {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 14px;
  box-sizing: border-box;
}

/* Title, add, menu. */
.bl-asm__phead {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 34px;
  flex: 0 0 auto;
}
.bl-asm__ptitle {
  width: 78px;
  height: 17px;
  border-radius: 5px;
  background: rgba(255, 255, 255, 0.82);
  margin-right: auto;
}
.bl-asm__hbtn {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  flex: 0 0 auto;
  background: rgba(255, 255, 255, 0.1);
}
.bl-asm__hbtn.is-solid {
  background: #f7f4f1;
}

/* The scene card. Low fidelity means the warm ground, the floor line, a
   soft shape where Bloom sits, and the two chips; not a drawing of the
   drawing. */
.bl-asm__scene {
  position: relative;
  flex: 0 0 auto;
  height: 146px;
  border-radius: 16px;
  overflow: hidden;
  background: linear-gradient(#efdccb 0%, #e8d2bd 58%, #d8bda4 58%, #d3b79d 100%);
}
.bl-asm__chip,
.bl-asm__focus {
  position: absolute;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.86);
}
.bl-asm__chip {
  left: 12px;
  top: 12px;
  width: 24px;
  height: 24px;
}
.bl-asm__focus {
  right: 12px;
  top: 10px;
  width: 44px;
  height: 44px;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5);
}
.bl-asm__bloom {
  position: absolute;
  left: 50%;
  bottom: 26px;
  width: 52px;
  height: 42px;
  transform: translateX(-50%);
  border-radius: 16px 16px 14px 14px;
  background: #f7f4f1;
}
.bl-asm__bloom::after {
  content: '';
  position: absolute;
  left: 50%;
  top: -13px;
  width: 30px;
  height: 18px;
  transform: translateX(-50%);
  border-radius: 15px 15px 4px 4px;
  background: #f3a3ad;
}

/* Habits | Tasks. */
.bl-asm__tabs {
  display: flex;
  gap: 4px;
  flex: 0 0 auto;
  padding: 3px;
  border-radius: 17px;
  background: rgba(255, 255, 255, 0.06);
}
.bl-asm__tabs i {
  flex: 1 1 0;
  height: 28px;
  border-radius: 14px;
}
.bl-asm__tabs i.is-on {
  background: #f7f4f1;
}

/* The rows. */
.bl-asm__rows {
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 0 0 auto;
}
.bl-asm__row {
  display: flex;
  align-items: center;
  gap: 11px;
  height: 56px;
  padding: 0 13px;
  box-sizing: border-box;
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.05);
}
.bl-asm__ico {
  width: 32px;
  height: 32px;
  border-radius: 9px;
  flex: 0 0 auto;
  background: rgba(255, 255, 255, 0.08);
}
.bl-asm__rtext {
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 1 1 auto;
}
.bl-asm__nm {
  height: 10px;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.5);
}
.bl-asm__st {
  height: 7px;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.2);
}
.bl-asm__nm--w70 {
  width: 70%;
}
.bl-asm__nm--w55 {
  width: 55%;
}
.bl-asm__nm--w40 {
  width: 40%;
}
.bl-asm__nm--w75 {
  width: 75%;
}
.bl-asm__st--w30 {
  width: 30%;
}
.bl-asm__st--w28 {
  width: 28%;
}
.bl-asm__st--w26 {
  width: 26%;
}
.bl-asm__rtick {
  width: 21px;
  height: 21px;
  flex: 0 0 auto;
}
.bl-asm__rtick--off {
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.11);
}

/* The streak sheet, which is where the month actually lives. */
.bl-asm__sheet {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 10px 14px 14px;
  box-sizing: border-box;
  border-radius: 22px 22px 0 0;
  background: #241b1b;
  box-shadow: 0 -18px 40px -20px rgba(0, 0, 0, 0.55);
}
.bl-asm__grab {
  width: 36px;
  height: 4px;
  border-radius: 2px;
  align-self: center;
  background: rgba(255, 255, 255, 0.18);
}
.bl-asm__monthbar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  height: 22px;
}
.bl-asm__monthbar b {
  width: 92px;
  height: 11px;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.42);
}
/* One glyph, and the back arrow is the mirror of it, which is how the app
   draws its own month bar. */
.bl-asm__arrow {
  width: 7px;
  height: 7px;
  border-right: 1.5px solid rgba(255, 255, 255, 0.34);
  border-bottom: 1.5px solid rgba(255, 255, 255, 0.34);
  transform: rotate(-45deg);
  flex: 0 0 auto;
}
.bl-asm__monthbar .bl-asm__arrow:first-child {
  transform: rotate(135deg);
}

/* The matrix. The app's own month view: seven columns, square cells, a
   rose on the days that were kept. */
.bl-asm__grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 3px;
  flex: 0 0 auto;
}
.bl-asm__wd {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 9px;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.3);
}
.bl-asm__cell {
  position: relative;
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 9px;
  background: rgba(255, 255, 255, 0.05);
}
.bl-asm__cell i {
  font-style: normal;
  font-size: 11px;
  font-weight: 550;
  color: rgba(255, 255, 255, 0.34);
}
.bl-asm__cell.is-blank {
  background: none;
}
.bl-asm__cell.is-future i {
  color: rgba(255, 255, 255, 0.16);
}
.bl-asm__cell.is-hit {
  background: rgba(20, 154, 120, 0.16);
}
/* Today is the square the section opens on, and for the first third of the
   timeline it is alone on the mint ground with no sheet behind it, so it
   cannot be a tint of a dark surface the way the other cells are: it needs
   a body of its own. Inside the finished sheet the same fill reads as a
   slightly raised cell, which is what today should look like anyway. */
.bl-asm__cell--lead {
  background: #262229;
}
.bl-asm__cell.is-today {
  box-shadow: 0 0 0 1.5px rgba(12, 179, 133, 0.75);
}
.bl-asm__rose {
  width: 62%;
  height: 62%;
  display: block;
}
.bl-asm__stats {
  display: flex;
  gap: 18px;
  height: 14px;
  flex: 0 0 auto;
}
.bl-asm__stats span {
  width: 62px;
  height: 11px;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.16);
}

/* The square while it is a word. index.html sets five 76px tiles into this
   same sentence and styles.css tuned line-height: 2.1 to clear them: at the
   42px top of the type clamp that is an 88px line box, so a 76px tile has
   12px to the row below. There is ONE square here and it is the subject of
   the section, so it is set large (owner, 2026-09-17), with more room left
   under it than Jobi needs: 64px leaves 24px, and the sentence needs no
   other change to carry it.

   KEEP IN STEP WITH SLOT in script.js (15). This rule reserves the box in
   the line; that constant is what scales the panel so the lead cell fills
   it. If they disagree the square sits off its own slot. */
.bl-asm__slot {
  --tile: 64px;
  border-radius: 18px;
}

/* On a phone: the sentence and the square in it, and nothing else.

   This page first tried to stand the whole panel up here, and on a narrow
   screen it was a mess (owner, 2026-09-17). index.html solved the same
   problem the same way months ago and that is the answer here too: drop
   the tiles entirely and let the slot draw its own block, so what is left
   is the first frame of the sequence, held still. No pin, no scrub, no
   panel, no browser.

   Everything about the stage, the caption and the line is already handled
   by styles.css's own 900px block, which this page's markup runs through
   unaltered. The only Bloomi-specific part is what the slot looks like:
   index.html's five carry a glyph on a coloured tile, and this one is the
   app's own cell, dark with the ring that means today. */
@media (max-width: 900px), (prefers-reduced-motion: reduce) {
  .bl-asm__tiles {
    display: none;
  }
  .bl-asm__slot {
    --tile: 52px;
    border-radius: 15px;
    display: inline-grid;
    place-items: center;
    background: #262229;
    box-shadow: 0 0 0 1.5px rgba(12, 179, 133, 0.75);
  }
  .bl-asm__slot svg {
    display: block;
    width: 62%;
    height: 62%;
  }
}
@media (max-width: 620px) {
  .bl-asm__slot {
    --tile: 42px;
    border-radius: 12px;
  }
}
