/* ============================================================
   Peptide Tracker UI system — "Doodle Journal"

   The whole app is a hand-kept notebook: engineer's graph paper,
   punched binder holes, handwriting, boxes drawn by hand, marker
   and tape. Nothing sits perfectly square, and ink draws itself
   in as you scroll.

   The mobile bottom nav keeps its liquid-glass treatment and lives
   in landing-glass-nav.css. It is the one translucent element.

   Class names are shared across every template, so this file is
   the single lever for the whole app's look.
   ============================================================ */

/* ---------- The paper ----------
   Engineer's pad: fine 20px squares with a heavier rule every 5th,
   painted on the body so every page sits on the same sheet. */

body {
    background-color: var(--background);
    background-image:
        /* paper grain, so the sheet is not a flat fill */
        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.045'/%3E%3C/svg%3E"),
        linear-gradient(to right, var(--grid-major) 1px, transparent 1px),
        linear-gradient(to bottom, var(--grid-major) 1px, transparent 1px),
        linear-gradient(to right, var(--grid-minor) 1px, transparent 1px),
        linear-gradient(to bottom, var(--grid-minor) 1px, transparent 1px);
    background-size: 140px 140px, 100px 100px, 100px 100px, 20px 20px, 20px 20px;
    background-position: 0 0, -1px -1px, -1px -1px, -1px -1px, -1px -1px;
}

/* ---------- Binder holes ----------
   A punched column down the left edge, repeating for the length of the
   page so it reads as one long sheet rather than a screen. */

.lg-holes {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0.55rem;
    width: 30px;
    z-index: 0;
    pointer-events: none;
    background-image: radial-gradient(circle at center,
        color-mix(in srgb, var(--foreground) 13%, var(--background)) 0 8px,
        color-mix(in srgb, var(--foreground) 22%, transparent) 8px 9px,
        transparent 9px);
    background-size: 30px 210px;
    background-repeat: repeat-y;
    background-position: center 90px;
}

@media (max-width: 1099px) {
    .lg-holes {
        display: none;
    }
}

/* ---------- Marker & tape ---------- */

/* Highlighter swipe — uneven ends, sits behind the text. */
.lg-hl {
    position: relative;
    display: inline;
    background-image: linear-gradient(
        104deg,
        transparent 0.4%,
        color-mix(in srgb, var(--accent) 26%, transparent) 2%,
        color-mix(in srgb, var(--accent) 32%, transparent) 96%,
        transparent 98.5%);
    background-size: 100% 62%;
    background-position: 0 72%;
    background-repeat: no-repeat;
    padding: 0 0.15em;
}

/* Sticky note — for asides and callouts. */
.lg-sticky {
    position: relative;
    display: block;
    padding: 1.1rem 1.15rem;
    background: #fdf0a8;
    color: #3d3417;
    border-radius: 2px 2px 14px 2px;
    box-shadow: 2px 3px 0 rgba(0, 0, 0, 0.10);
    transform: rotate(-1.2deg);
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-size: 1.2rem;
    line-height: 1.35;
}

.lg-sticky--right {
    transform: rotate(1.4deg);
}

/* ---------- Hiding a button ----------
   Tailwind emits .hidden inside @layer utilities, so .lg-btn's unlayered
   display:inline-flex outranks it and the button stays on screen. Five
   dashboard templates hide a Remove/Cancel button this way and none of them
   worked.

   Scoped to .lg-btn rather than asserting .hidden globally with !important:
   18 places in this codebase use the `hidden sm:flex` / `hidden lg:block`
   pattern for responsive panels, and !important would beat those variants
   too, leaving desktop-only content hidden everywhere. */
.lg-btn.hidden {
    display: none;
}

/* ---------- Tape ----------
   Translucent strips that hold a card down to the sheet. The grid shows
   faintly through, and the ends are torn rather than cut. Every taped
   surface gets one automatically; position and angle vary so no two
   look applied by the same motion. */

.lg-tape,
.lg-tile::after,
.lg-panel::after {
    content: "";
    position: absolute;
    z-index: 3;
    width: 5rem;
    height: 1.6rem;
    pointer-events: none;
    background:
        linear-gradient(180deg,
            color-mix(in srgb, #fffdf2 72%, transparent),
            color-mix(in srgb, #ebe7d2 62%, transparent));
    box-shadow: 0 1px 2px color-mix(in srgb, var(--foreground) 12%, transparent);
    /* torn ends, not scissor-cut */
    clip-path: polygon(2% 8%, 8% 0%, 100% 5%, 96% 94%, 6% 100%, 0% 88%);
}

/* Panels: one strip centred on the top edge. */
.lg-panel::after {
    top: -0.8rem;
    left: 50%;
    margin-left: -2.5rem;
    transform: rotate(-2.6deg);
}

/* Tiles: taped at a corner, alternating side and angle. */
.lg-tile::after {
    top: -0.7rem;
    left: -0.9rem;
    width: 4.25rem;
    height: 1.4rem;
    transform: rotate(-34deg);
}

.lg-tile:nth-child(even)::after {
    left: auto;
    right: -0.9rem;
    transform: rotate(32deg);
}

.lg-tile:nth-child(3n)::after {
    top: -0.75rem;
    left: 50%;
    right: auto;
    margin-left: -2.1rem;
    transform: rotate(-3.5deg);
}

/* Opt out where tape would crowd the layout. */
.lg-tile--untaped::after,
.lg-panel--untaped::after {
    content: none;
}

/* Legacy hook, kept so existing markup keeps working. */
.lg-taped {
    position: relative;
}

/* ---------- Links: underlined by hand on hover ----------
   A squiggle is drawn left-to-right under a text link when you point at
   it. Buttons, cards and the brand are excluded — they are surfaces, not
   words in a sentence. */

.landing-page a:not(.lg-btn):not(.lg-tile):not(.lg-panel):not([class*="flex"]):not([class*="grid"]):not([class*="block"]),
.marketing-page-shell a:not(.lg-btn):not(.lg-tile):not(.lg-panel):not([class*="flex"]):not([class*="grid"]):not([class*="block"]) {
    position: relative;
    text-decoration: none;
}

.landing-page a:not(.lg-btn):not(.lg-tile):not(.lg-panel):not([class*="flex"]):not([class*="grid"]):not([class*="block"])::after,
.marketing-page-shell a:not(.lg-btn):not(.lg-tile):not(.lg-panel):not([class*="flex"]):not([class*="grid"]):not([class*="block"])::after {
    content: "";
    position: absolute;
    left: -3%;
    right: -3%;
    bottom: -0.28em;
    height: 0.36em;
    pointer-events: none;
    background-repeat: no-repeat;
    background-size: 100% 100%;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 14' preserveAspectRatio='none'%3E%3Cpath d='M3 9c34-6 62 4 96-2 30-5 62 5 98-2' fill='none' stroke='%230b7a55' stroke-width='3.2' stroke-linecap='round'/%3E%3C/svg%3E");
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform 260ms cubic-bezier(0.5, 0, 0.35, 1);
}

.landing-page a:not(.lg-btn):not(.lg-tile):not(.lg-panel):not([class*="flex"]):not([class*="grid"]):not([class*="block"]):hover::after,
.landing-page a:not(.lg-btn):not(.lg-tile):not(.lg-panel):not([class*="flex"]):not([class*="grid"]):not([class*="block"]):focus-visible::after,
.marketing-page-shell a:not(.lg-btn):not(.lg-tile):not(.lg-panel):not([class*="flex"]):not([class*="grid"]):not([class*="block"]):hover::after,
.marketing-page-shell a:not(.lg-btn):not(.lg-tile):not(.lg-panel):not([class*="flex"]):not([class*="grid"]):not([class*="block"]):focus-visible::after {
    transform: scaleX(1);
}

/* The brand lockup and primary nav are marks, not sentences. */
.lg-header a::after {
    content: none !important;
}

/* Nav links get the squiggle, but only across the label itself. */
.lg-header__links a::after {
    content: "" !important;
    left: 0.7rem;
    right: 0.7rem;
    bottom: 0.1rem;
}

@media (prefers-reduced-motion: reduce) {
    .landing-page a::after,
    .marketing-page-shell a::after {
        transition: none;
    }
}

/* Hand-drawn squiggle underline, for pulling out a phrase. */
.lg-underline {
    position: relative;
    display: inline-block;
}

.lg-underline::after {
    content: "";
    position: absolute;
    left: -2%;
    right: -2%;
    bottom: -0.35em;
    height: 0.42em;
    background-repeat: no-repeat;
    background-size: 100% 100%;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 14' preserveAspectRatio='none'%3E%3Cpath d='M3 9c34-6 62 3 96-2 30-4 62 4 98-1' fill='none' stroke='%230b7a55' stroke-width='3.4' stroke-linecap='round'/%3E%3C/svg%3E");
}

/* ---------- Ink that draws itself ----------
   Any SVG path marked [data-sketch] starts undrawn and inks in when it
   scrolls into view (sketch.js adds .is-drawn). pathLength="1" lets one
   rule cover paths of any real length. */

[data-sketch] path,
[data-sketch] line,
[data-sketch] circle,
[data-sketch] ellipse,
[data-sketch] polyline {
    stroke-dasharray: 1;
    stroke-dashoffset: 1;
}

[data-sketch].is-drawn path,
[data-sketch].is-drawn line,
[data-sketch].is-drawn circle,
[data-sketch].is-drawn ellipse,
[data-sketch].is-drawn polyline {
    stroke-dashoffset: 0;
    transition: stroke-dashoffset 1.1s cubic-bezier(0.55, 0, 0.35, 1);
}

/* Stagger multi-stroke drawings so they build stroke by stroke. */
[data-sketch].is-drawn *:nth-child(2) { transition-delay: 0.16s; }
[data-sketch].is-drawn *:nth-child(3) { transition-delay: 0.32s; }
[data-sketch].is-drawn *:nth-child(4) { transition-delay: 0.46s; }
[data-sketch].is-drawn *:nth-child(n+5) { transition-delay: 0.6s; }

/* ---------- Margin doodles ----------
   Sketches in the page margins, like someone annotated their own notes.
   Hidden on narrow screens where there is no margin to draw in. */

/* Anchored to the outside edge of the 64rem content column so a doodle
   can never collide with the writing, whatever the viewport width. */
.lg-doodle {
    position: absolute;
    color: var(--accent-ink, var(--accent));
    opacity: 0.75;
    pointer-events: none;
    display: none;
}

.lg-doodle--left {
    right: calc(50% + 30rem);
}

.lg-doodle--right {
    left: calc(50% + 30rem);
}

.lg-doodle svg {
    width: 100%;
    height: auto;
    overflow: visible;
}

.lg-doodle__note {
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-size: 1.05rem;
    font-weight: 600;
    line-height: 1.2;
    color: var(--muted-foreground);
    transform: rotate(-6deg);
    transform-origin: left top;
    display: block;
    margin-top: 0.35rem;
    max-width: 100%;
    overflow-wrap: break-word;
}

@media (min-width: 1240px) {
    .lg-doodle {
        display: block;
    }
}

/* On roomier screens the margins can carry a bigger sketch. */
@media (min-width: 1500px) {
    .lg-doodle--left {
        right: calc(50% + 32rem);
    }

    .lg-doodle--right {
        left: calc(50% + 32rem);
    }
}

/* ---------- Motif primitives ---------- */

/* Syringe graduation ticks — the signature rule of the system. */
.lg-ticks {
    height: 0.6rem;
    background-image: repeating-linear-gradient(
        to right,
        color-mix(in srgb, var(--foreground) 26%, transparent) 0 1px,
        transparent 1px 0.5rem);
    -webkit-mask-image: linear-gradient(to right, #000 60%, transparent);
    mask-image: linear-gradient(to right, #000 60%, transparent);
}

.lg-ticks--accent {
    background-image: repeating-linear-gradient(
        to right,
        var(--accent) 0 2px,
        transparent 2px 0.5rem);
}

/* Kicker: measured label. The leading accent bar it used to carry read as
   a stray dash in front of the words and interrupted the run into the
   heading below, so it is gone; the only kickers left are the footer's
   column headings. */
.lg-kicker {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    font-size: 0.6875rem;
    font-weight: 800;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--accent-ink, var(--accent));
}

/* ---------- Brand mark ----------
   A solid accent block with a dose barrel cut out of it. The fluid
   rises on load and refills on hover; a droplet falls at the tip. */

.lg-mark {
    display: inline-flex;
    width: 2.25rem;
    height: 2.25rem;
    flex: 0 0 auto;
}

.lg-mark svg {
    width: 100%;
    height: 100%;
    overflow: visible;
}

.lg-mark--lg {
    width: 3.25rem;
    height: 3.25rem;
}

.lg-mark__fluid {
    transform-origin: bottom center;
    animation: lgMarkFill 2.6s cubic-bezier(0.16, 1, 0.3, 1) 0.2s both;
}

.lg-mark__drop {
    opacity: 0;
    animation: lgMarkDrop 2.6s ease-in 0.2s both;
}

/* Re-run both on hover so the mark feels alive when you reach for it. */
.lg-mark:hover .lg-mark__fluid,
a:hover > .lg-mark .lg-mark__fluid {
    animation: lgMarkFill 1.5s cubic-bezier(0.16, 1, 0.3, 1) both;
}

.lg-mark:hover .lg-mark__drop,
a:hover > .lg-mark .lg-mark__drop {
    animation: lgMarkDrop 1.5s ease-in both;
}

@keyframes lgMarkFill {
    0%       { transform: scaleY(0); }
    58%, 78% { transform: scaleY(1); }
    100%     { transform: scaleY(0.72); }
}

@keyframes lgMarkDrop {
    0%, 74%  { opacity: 0; transform: translateY(-3px) scale(0.5); }
    84%      { opacity: 1; transform: translateY(0) scale(1); }
    100%     { opacity: 0; transform: translateY(6px) scale(0.7); }
}

@media (prefers-reduced-motion: reduce) {
    .lg-mark__fluid {
        animation: none;
        transform: scaleY(0.72);
    }

    .lg-mark__drop {
        animation: none;
        opacity: 0;
    }
}

/* ---------- Surfaces ---------- */

/* Boxes drawn by hand: uneven corner radii read as pen strokes rather
   than vector rectangles. Radii vary per component so no two boxes on
   a page trace the same outline. */

.lg-panel {
    position: relative;
    border: 2px solid var(--foreground);
    border-radius: 255px 14px 225px 15px / 15px 225px 15px 255px;
    background: var(--card);
}

.lg-tile {
    position: relative;
    display: block;
    padding: 1.35rem;
    border: 2px solid var(--foreground);
    border-radius: 225px 15px 255px 16px / 16px 255px 15px 225px;
    background: var(--card);
    transition:
        transform 200ms cubic-bezier(0.16, 1, 0.3, 1),
        box-shadow 200ms ease,
        background-color 200ms ease;
}

/* Every other box leans the other way, like a hand that got tired. */
.lg-tile:nth-child(even) {
    border-radius: 15px 225px 15px 255px / 255px 15px 225px 15px;
}

/* Nothing on a hand-made page sits perfectly square. Rotations are tiny
   and cycle on a prime-ish interval so no two neighbours match. */
.lg-tile:nth-child(3n + 1) { transform: rotate(-0.45deg); }
.lg-tile:nth-child(3n + 2) { transform: rotate(0.3deg); }
.lg-tile:nth-child(5n)     { transform: rotate(0.55deg); }

.lg-panel {
    transform: rotate(-0.18deg);
}

/* Drop-shadow in pen, offset like the page lifted slightly. */
.lg-panel,
.lg-tile {
    box-shadow: 3px 4px 0 color-mix(in srgb, var(--foreground) 12%, transparent);
}

.lg-tile:hover {
    transform: translate(-2px, -3px) rotate(-0.35deg);
    background: var(--card-hover);
    box-shadow: 4px 5px 0 color-mix(in srgb, var(--accent) 55%, transparent);
}

.lg-tile--compact {
    padding: 0.9rem 0.6rem;
}

.lg-tile--static:hover {
    border-color: var(--border);
    background: var(--card);
    transform: none;
}

.lg-tile--static::before {
    content: none;
}

/* ---------- Icon plates ---------- */

/* Icons are drawn on the page, not printed chips. The old per-icon tints
   (--lg-tint) are deliberately ignored: a purple or yellow plate reads as
   a UI component and breaks the hand-drawn illusion. Everything is ink
   and pen-green, boxed in an uneven hand-drawn square. */
.lg-icon-tile {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    flex: 0 0 auto;
    border: 2px solid var(--foreground);
    border-radius: 14px 4px 12px 5px / 5px 12px 4px 14px;
    background: transparent;
    color: var(--accent-ink, var(--accent));
}

.lg-icon-tile--sm {
    width: 2.25rem;
    height: 2.25rem;
    border-radius: 11px 4px 10px 4px / 4px 10px 4px 11px;
}

/* Thinner, rounder strokes read as pen rather than vector. CSS wins over
   the stroke-width attributes baked into the icon markup. */
.lg-icon-tile svg {
    stroke-width: 1.75;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* ---------- Buttons ---------- */

.lg-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.75rem 1.5rem;
    border-radius: 255px 15px 225px 15px / 15px 225px 15px 255px;
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-weight: 700;
    font-size: 1.25rem;
    line-height: 1.1;
    letter-spacing: 0.01em;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition:
        transform 140ms cubic-bezier(0.16, 1, 0.3, 1),
        box-shadow 140ms ease,
        background-color 140ms ease;
}

.lg-btn:hover {
    transform: translate(-1px, -2px) rotate(-0.5deg);
}

.lg-btn:active {
    transform: translate(1px, 1px);
    box-shadow: none;
}

.lg-btn--sm {
    padding: 0.4rem 1rem;
    font-size: 1.05rem;
}

/* Primary: filled in with the pen. */
.lg-btn--primary {
    border: 2px solid var(--foreground);
    background: var(--accent-ink, var(--accent));
    color: var(--accent-foreground);
    box-shadow: 3px 4px 0 var(--foreground);
}

.lg-btn--primary:hover {
    background: var(--accent-dark);
    box-shadow: 5px 6px 0 var(--foreground);
}

/* Secondary: outlined only. */
.lg-btn--glass {
    border: 2px solid var(--foreground);
    background: var(--card);
    color: var(--foreground);
    box-shadow: 3px 4px 0 color-mix(in srgb, var(--foreground) 22%, transparent);
}

.lg-btn--glass:hover {
    background: var(--card-hover);
    box-shadow: 5px 6px 0 color-mix(in srgb, var(--foreground) 30%, transparent);
}

.lg-btn--danger {
    border: 2px solid #a3301f;
    background: var(--card);
    color: #a3301f;
    box-shadow: 3px 4px 0 color-mix(in srgb, #a3301f 28%, transparent);
}

.lg-btn--danger:hover {
    background: color-mix(in srgb, #b3382b 10%, var(--card));
    box-shadow: 5px 6px 0 color-mix(in srgb, #a3301f 34%, transparent);
}

/* A row of buttons should read as one row: fix a height per size so
   variants cannot drift apart when mixed. */
.lg-btn {
    min-height: 2.85rem;
}

.lg-btn--sm {
    min-height: 2.35rem;
}

.lg-btn--ghost {
    min-height: 0;
}

/* Plain underlined text — for a quiet action that should not look like a
   button at all. */
.lg-textlink {
    display: inline-block;
    padding: 0.25rem 0;
    border: 0;
    background: none;
    color: var(--muted-foreground);
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-weight: 700;
    font-size: 1.15rem;
    line-height: 1.2;
    text-decoration: underline;
    text-decoration-thickness: 1.5px;
    text-underline-offset: 3px;
    cursor: pointer;
    transition: color 140ms ease;
}

.lg-textlink:hover {
    color: var(--foreground);
}

.lg-btn--ghost {
    padding: 0.5rem;
    border: 0;
    background: transparent;
    color: var(--muted-foreground);
    border-radius: 0.5rem;
}

.lg-btn--ghost:hover {
    color: var(--foreground);
    background: color-mix(in srgb, var(--foreground) 8%, transparent);
    transform: none;
}

/* ---------- Inputs ---------- */

.lg-input {
    width: 100%;
    padding: 0.7rem 0.9rem;
    border: 2px solid color-mix(in srgb, var(--foreground) 55%, transparent);
    border-radius: 14px 5px 12px 6px / 6px 12px 5px 14px;
    background: var(--background);
    color: var(--foreground);
    font-size: 1rem;
    transition: border-color 140ms ease, box-shadow 140ms ease;
}

.lg-input::placeholder {
    color: color-mix(in srgb, var(--muted-foreground) 75%, transparent);
}

.lg-input:focus {
    outline: none;
    border-color: var(--accent-ink, var(--accent));
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent);
}

/* ---------- Rules & helpers ---------- */

.lg-hairline {
    border-color: var(--border);
}

/* Retired glow — kept as a no-op so older markup degrades cleanly. */
.lg-glow {
    display: none;
}

/* ---------- Rows ---------- */

.lg-mock-row {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.75rem;
    border: 1px solid var(--border);
    border-radius: 0.625rem;
    background: var(--background);
}

.lg-mock-row--stack {
    display: block;
    padding: 1rem;
}

/* ---------- Measure bar (syringe barrel) ----------
   A progress track drawn as a graduated barrel with a plunger fill.
   Set --lg-fill (0-100%) on the element. */

.lg-measure {
    position: relative;
    height: 0.5rem;
    overflow: hidden;
    border: 1px solid var(--border);
    border-radius: 0.25rem;
    background: var(--background);
}

.lg-measure::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: var(--lg-fill, 0%);
    background: var(--accent);
    animation: lgPlunger 900ms cubic-bezier(0.16, 1, 0.3, 1) both;
}

.lg-measure::after {
    content: "";
    position: absolute;
    inset: 0;
    background-image: repeating-linear-gradient(
        to right,
        color-mix(in srgb, var(--background) 80%, transparent) 0 1px,
        transparent 1px 0.75rem);
}

@keyframes lgPlunger {
    from { width: 0; }
}

/* ---------- Scribbled progress line ----------
   A level drawn in with a pen rather than a filled bar. The wobble lives
   in the background image, and the reveal is a clip so the wobble stays
   aligned with the track underneath. Set --lg-fill (a percentage).
   Mark the element [data-ink] and sketch.js draws it on scroll. */

.lg-scribble {
    position: relative;
    height: 0.7rem;
    background-repeat: no-repeat;
    background-size: 100% 100%;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 12' preserveAspectRatio='none'%3E%3Cpath d='M4 7c48-3 96 2 146-1 52-3 100 3 146 0' fill='none' stroke='%231b2a22' stroke-width='2.4' stroke-linecap='round' opacity='.16'/%3E%3C/svg%3E");
}

.lg-scribble__ink {
    position: absolute;
    inset: 0;
    background-repeat: no-repeat;
    background-size: 100% 100%;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 12' preserveAspectRatio='none'%3E%3Cpath d='M4 7c48-3 96 2 146-1 52-3 100 3 146 0' fill='none' stroke='%230b7a55' stroke-width='4' stroke-linecap='round'/%3E%3C/svg%3E");
    /* hidden until drawn */
    clip-path: inset(0 100% 0 0);
}

/* Slow, uneven easing so it reads as a hand travelling across the page. */
[data-ink].is-drawn .lg-scribble__ink,
.lg-scribble[data-ink].is-drawn .lg-scribble__ink {
    clip-path: inset(0 calc(100% - var(--lg-fill, 0%)) 0 0);
    transition: clip-path 1.5s cubic-bezier(0.45, 0.05, 0.35, 1);
}

/* A warning level is drawn in the same hand, different ink. */
.lg-scribble--warn .lg-scribble__ink {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 12' preserveAspectRatio='none'%3E%3Cpath d='M4 7c48-3 96 2 146-1 52-3 100 3 146 0' fill='none' stroke='%23c2740a' stroke-width='4' stroke-linecap='round'/%3E%3C/svg%3E");
}

@media (prefers-reduced-motion: reduce) {
    .lg-scribble__ink {
        clip-path: inset(0 calc(100% - var(--lg-fill, 0%)) 0 0);
        transition: none;
    }
}


/* ---------- Dashboard: same journal voice as the public pages ----------
   The app is part of the same notebook, so headings are handwritten here
   too. Data stays typeset and mono so daily logging stays legible. */

:where(.dashboard-app-shell, .journal-app, .calculator-page) :where(h1, h2, h3, h4, h5),
:where(.dashboard-app-shell, .journal-app, .calculator-page) .heading-font {
    font-family: 'Caveat', 'Bricolage Grotesque', ui-sans-serif, cursive;
    font-weight: 700;
    letter-spacing: 0;
    line-height: 1.05;
    text-transform: none;
    color: var(--foreground);
}

.dashboard-app-shell h1 { font-size: 2.6rem; }
.dashboard-app-shell h2 { font-size: 1.9rem; }
.dashboard-app-shell h3 { font-size: 1.5rem; }

/* Modals, toasts and banners sit at body level and are physically small,
   so they get a tighter ladder than a full page heading. Tailwind's
   preflight resets headings to font-size: inherit, so without this a
   dialog title renders smaller than its own field label. */
.journal-app :is(.modal-backdrop, #update-banner, #install-banner, #feedback-widget) h1 { font-size: 1.9rem; }
.journal-app :is(.modal-backdrop, #update-banner, #install-banner, #feedback-widget) h2 { font-size: 1.75rem; }
.journal-app :is(.modal-backdrop, #update-banner, #install-banner, #feedback-widget) h3 { font-size: 1.5rem; }
.journal-app :is(.modal-backdrop, #update-banner, #install-banner, #feedback-widget) h4 { font-size: 1.3rem; }

/* Numbers, units and tables must stay precise. */
:where(.dashboard-app-shell, .journal-app) :where(.font-data, table, td, th, input, select, textarea) {
    font-family: 'JetBrains Mono', ui-monospace, monospace;
}

:where(.dashboard-app-shell, .journal-app) :where(p, li) {
    font-family: 'Figtree', ui-sans-serif, system-ui, sans-serif;
}

/* A field label names a thing, so it is handwritten — the same split the
   marketing pages use. Help text nested inside a label is a sentence and
   stays typeset. */
:where(.dashboard-app-shell, .journal-app) :where(label) {
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-size: 1.15rem;
    font-weight: 700;
    letter-spacing: 0.01em;
}

:where(.dashboard-app-shell, .journal-app) label :where(p, span.text-xs, .text-muted-foreground, .info-tooltip-content) {
    font-family: 'Figtree', ui-sans-serif, system-ui, sans-serif;
    font-weight: 400;
    font-size: 0.75rem;
    letter-spacing: 0;
}

/* ---------- Page headers: a different hand per page ----------
   A notebook does not title every page the same way. Each dashboard page
   picks one of these treatments. All share .lg-head. */

.lg-head {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.85rem;
    /* Circles and underlines overshoot the text box, so the header needs
       headroom or the decoration clips against the top of the page. */
    padding-top: 1.25rem;
    margin-bottom: 1.75rem;
}

@media (min-width: 1024px) {
    .lg-head {
        padding-top: 1.75rem;
    }
}

.lg-head__title {
    position: relative;
    display: inline-block;
}

.lg-head__sub {
    font-family: 'Figtree', ui-sans-serif, system-ui, sans-serif;
    font-size: 0.75rem;
    line-height: 1.25;
    color: var(--muted-foreground);
}

/* 1. Underlined — a squiggle ruled under the title. */
.lg-head--underline .lg-head__title::after {
    content: "";
    position: absolute;
    left: -2%;
    right: -4%;
    bottom: -0.18em;
    height: 0.34em;
    background-repeat: no-repeat;
    background-size: 100% 100%;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 14' preserveAspectRatio='none'%3E%3Cpath d='M3 8c34-5 62 4 96-1 30-5 62 4 98-2' fill='none' stroke='%230b7a55' stroke-width='3.4' stroke-linecap='round'/%3E%3C/svg%3E");
}

/* 2. Circled — ringed twice with a pen. */
.lg-head--circle .lg-head__title {
    padding: 0.15em 0.6em 0.25em;
}

.lg-head--circle .lg-head__title::after {
    content: "";
    position: absolute;
    inset: -18% -8%;
    background-repeat: no-repeat;
    background-size: 100% 100%;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 80' preserveAspectRatio='none'%3E%3Cpath d='M104 5C52 4 12 20 11 40c-1 21 44 36 92 35 45-1 82-16 82-33C185 22 150 6 104 5' fill='none' stroke='%230b7a55' stroke-width='2.6' stroke-linecap='round' opacity='.6'/%3E%3Cpath d='M96 9C46 11 18 24 18 41c0 18 38 30 82 29' fill='none' stroke='%230b7a55' stroke-width='2' stroke-linecap='round' opacity='.45'/%3E%3C/svg%3E");
}

/* 3. Highlighted — marker swiped across the title. */
.lg-head--highlight .lg-head__title {
    padding: 0 0.3em;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 44' preserveAspectRatio='none'%3E%3Cpath d='M6 27C13 13 34 10 62 11c30 1 76-5 108-2 15 1 24 6 22 15-2 10-40 12-92 12-38 0-84 3-92-2-6-3-6-9-2-7Z' fill='%230b7a55' opacity='.26'/%3E%3C/svg%3E");
    background-size: 100% 74%;
    background-position: 0 68%;
    background-repeat: no-repeat;
}

/* 3b. Warn — for a paused / needs-attention control. */
.lg-btn--warn {
    border: 2px solid var(--foreground);
    background: #b07d16;
    color: #fff;
    box-shadow: 3px 4px 0 var(--foreground);
}

.lg-btn--warn:hover {
    background: #96690f;
    box-shadow: 5px 6px 0 var(--foreground);
}

/* Peptide colour, shown as an inked dot beside the name rather than a
   slab of colour down the edge of the row. */
.lg-dot {
    display: inline-block;
    width: 0.85rem;
    height: 0.85rem;
    flex: 0 0 auto;
    border: 1.5px solid var(--foreground);
    border-radius: 45% 55% 52% 48% / 52% 48% 55% 45%;
}

/* 4. Tab — the title on a taped index tab. */
.lg-head--tab {
    padding-top: 0.4rem;
}

.lg-head--tab .lg-head__title {
    padding: 0.3em 1em 0.35em;
    background: var(--card);
    border: 2px solid var(--foreground);
    border-radius: 14px 14px 3px 3px / 12px 12px 3px 3px;
    box-shadow: 3px 3px 0 color-mix(in srgb, var(--foreground) 12%, transparent);
    transform: rotate(-0.8deg);
}

/* 5. Boxed — squared off by hand. */
.lg-head--box .lg-head__title {
    padding: 0.15em 0.7em 0.25em;
    border: 2px solid var(--foreground);
    border-radius: 225px 12px 255px 14px / 14px 255px 12px 225px;
}

/* 6. Noted — an arrow doodle points at the title. */
.lg-head--note .lg-head__title::after {
    content: "";
    position: absolute;
    right: -3.4rem;
    top: 50%;
    width: 3rem;
    height: 2rem;
    margin-top: -1rem;
    background-repeat: no-repeat;
    background-size: 100% 100%;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 60 40'%3E%3Cpath d='M56 20C44 8 24 10 8 20' fill='none' stroke='%230b7a55' stroke-width='3' stroke-linecap='round'/%3E%3Cpath d='M8 20l12-4M8 20l10 8' fill='none' stroke='%230b7a55' stroke-width='3' stroke-linecap='round'/%3E%3C/svg%3E");
}

@media (max-width: 639px) {
    .lg-head--note .lg-head__title::after { display: none; }
    .dashboard-app-shell h1 { font-size: 2.2rem; }
}

/* ---------- Emoji avatar ----------
   A sticker on the page rather than a UI chip: hand-drawn edge, tilted,
   with the colour supplied per-account from avatar.py. */

.lg-avatar {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    flex: 0 0 auto;
    font-size: 1.15rem;
    line-height: 1;
    border: 2px solid var(--foreground);
    border-radius: 40% 60% 55% 45% / 55% 45% 60% 40%;
    transform: rotate(-3deg);
}

.lg-avatar--lg {
    width: 3rem;
    height: 3rem;
    font-size: 1.5rem;
}

/* ---------- Streak flame ----------
   The streak reads as a lit match in the margin rather than a number in
   a caption: a drawn flame with the count written next to it, tallying
   up from zero on load. Warm ink, because a green flame is not a flame. */

.lg-streak {
    display: flex;
    align-items: baseline;
    gap: 0.3rem;
    margin-top: 0.1rem;
    white-space: nowrap;
}

.lg-streak__flame {
    width: 1.05rem;
    height: 1.4rem;
    flex: 0 0 auto;
    align-self: center;
    overflow: visible;
    transform-origin: 50% 92%;
}

/* Outline drawn in warm ink; the core is the hot part. */
.lg-streak__outline {
    fill: none;
    stroke: #d4614f;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.lg-streak__core {
    fill: #f6a94a;
    transform-origin: 50% 85%;
}

/* A slow, low-amplitude flicker — enough to read as alive, not enough to
   pull the eye away from the page. */
.lg-streak__flame {
    animation: lgFlameSway 3.4s ease-in-out infinite;
}

.lg-streak__core {
    animation: lgFlameCore 1.7s ease-in-out infinite;
}

@keyframes lgFlameSway {
    0%, 100% { transform: rotate(-2.5deg) scaleY(1); }
    50%      { transform: rotate(2.5deg) scaleY(1.05); }
}

@keyframes lgFlameCore {
    0%, 100% { transform: scale(1) translateY(0); opacity: 0.95; }
    50%      { transform: scale(0.88) translateY(1px); opacity: 0.75; }
}

/* A cold streak still shows the flame, unlit — the shape stays put so the
   card does not reflow the day it catches. */
.lg-streak[data-streak="0"] .lg-streak__core {
    fill: color-mix(in srgb, var(--foreground) 18%, transparent);
    animation: none;
}

.lg-streak[data-streak="0"] .lg-streak__outline {
    stroke: color-mix(in srgb, var(--foreground) 35%, transparent);
}

.lg-streak[data-streak="0"] .lg-streak__flame {
    animation: none;
}

.lg-streak__count {
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-weight: 700;
    font-size: 1.3rem;
    line-height: 1;
    color: var(--foreground);
    font-variant-numeric: tabular-nums;
}

/* The final tick lands with a small pop, like pressing the pen down. */
.lg-streak__count.is-landed {
    animation: lgTallyLand 260ms cubic-bezier(0.34, 1.7, 0.5, 1) both;
}

@keyframes lgTallyLand {
    0%   { transform: scale(1); }
    45%  { transform: scale(1.18) rotate(-2deg); }
    100% { transform: scale(1) rotate(0); }
}

.lg-streak__unit {
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-weight: 700;
    font-size: 1.05rem;
    line-height: 1;
    color: var(--muted-foreground);
}

@media (prefers-reduced-motion: reduce) {
    .lg-streak__flame,
    .lg-streak__core,
    .lg-streak__count.is-landed {
        animation: none;
    }
}

/* ---------- Stat captions ----------
   Written under a figure the way you'd label a sketch, replacing the old
   uppercase micro-labels which read as UI chrome. */

.lg-stat-caption {
    display: block;
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-weight: 700;
    font-size: 1.35rem;
    line-height: 1.1;
    color: var(--foreground);
    text-transform: none;
    letter-spacing: 0;
}

/* Tailwind's position utilities are single-class, exactly like .lg-panel
   and .lg-tile, and ui.css loads after tailwind.css — so a taped panel
   that is meant to be fixed or absolute silently became relative. The
   update/install banners landed in the page flow because of this. */

.lg-panel.fixed,
.lg-tile.fixed {
    position: fixed;
}

.lg-panel.absolute,
.lg-tile.absolute {
    position: absolute;
}

.lg-panel.sticky,
.lg-tile.sticky {
    position: sticky;
}

/* The quick-action row was capped at 46rem inside a max-w-5xl page, so it
   stopped well short of the calendar panel below it and left a wide gap
   down the right-hand side. Let the three tiles span the same measure as
   everything else on the page. */
.dashboard-action-grid {
    max-width: none;
}

/* ---------- Quick action tiles ----------
   theme.css builds these from accent gradients and a tinted icon plate,
   which is the one look the redesign replaced everywhere else. Redrawn
   here as pen boxes; ui.css loads after theme.css so plain overrides win,
   except on the icon plate where theme.css uses !important. */

.tracker-tool-card {
    border: 2px solid var(--foreground);
    border-radius: 255px 15px 225px 15px / 15px 225px 15px 255px;
    background: var(--card);
    box-shadow: 3px 4px 0 color-mix(in srgb, var(--foreground) 20%, transparent);
    transition:
        transform 140ms cubic-bezier(0.16, 1, 0.3, 1),
        box-shadow 140ms ease,
        background-color 140ms ease;
}

/* Alternating tilt so a row of three does not look stamped. */
.tracker-tool-card:nth-child(odd) { transform: rotate(-0.5deg); }
.tracker-tool-card:nth-child(even) { transform: rotate(0.4deg); }

.tracker-tool-card:hover,
.tracker-tool-card:focus-visible {
    border-color: var(--foreground);
    background: var(--card-hover);
    box-shadow: 5px 6px 0 color-mix(in srgb, var(--foreground) 28%, transparent);
    transform: translate(-1px, -2px) rotate(-0.6deg);
}

.tracker-tool-card:active {
    transform: translate(1px, 1px);
    box-shadow: none;
}

.tracker-tool-icon {
    border: 2px solid var(--foreground);
    border-radius: 12px 4px 11px 4px / 4px 11px 4px 12px;
    background: transparent !important;
    box-shadow: none;
    color: var(--accent-ink, var(--accent)) !important;
}

.tracker-tool-icon svg {
    stroke-width: 1.75;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.tracker-tool-title,
.dashboard-action-card .tracker-tool-title {
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-weight: 700;
    font-size: 1.4rem;
    line-height: 1.05;
}

/* The due count is a measured value, so it stays mono. */
.tracker-tool-title.font-data {
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-size: 1.15rem;
}

.tracker-tool-subtitle {
    font-family: 'Figtree', ui-sans-serif, system-ui, sans-serif;
    font-size: 0.75rem;
    line-height: 1.25;
}

/* ---------- Calendar: ruled and drawn by hand ----------
   Someone laying out a month in a notebook rules the boxes themselves,
   so every cell is a drawn box and today is circled in pen. */

.calendar-title {
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-weight: 700;
    font-size: 1.6rem;
    line-height: 1;
    color: var(--foreground);
}

.calendar-dow {
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-weight: 700;
    font-size: 1.15rem;
    line-height: 1;
    color: color-mix(in srgb, var(--foreground) 65%, transparent);
    text-align: center;
    padding-block: 0.25rem;
}

/* A CSS border is always a straight line, however uneven the corner
   radius — so the cell outline is a stretched SVG instead, which keeps
   its wobble at any cell size (preserveAspectRatio="none"). */
.calendar-day {
    position: relative;
    border: 0;
    background: transparent;
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-weight: 700;
    font-size: 1.15rem;
    line-height: 1.1;
    transition: background-color 120ms ease;
}

.calendar-day::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background-repeat: no-repeat;
    background-size: 100% 100%;
    opacity: 0.5;
    /* Ruled by hand: edges bow slightly and corners overshoot. */
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='none'%3E%3Cpath d='M4 6C28 3 62 4 96 5M95 4c2 27 1 60 1 92M97 94c-30 2-63 1-93 2M5 96C3 70 4 35 4 5' fill='none' stroke='%231b2a22' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
}

/* A second hand for alternating cells, so no two neighbours are traced
   from the same line. */
.calendar-day:nth-child(even)::after {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='none'%3E%3Cpath d='M5 4c30 4 63 2 92 3M96 6c1 29 3 58 1 89M96 93c-31 3-61 2-92 3M4 95C6 66 3 36 5 5' fill='none' stroke='%231b2a22' stroke-width='1.4' stroke-linecap='round'/%3E%3C/svg%3E");
}

.calendar-day:nth-child(3n)::after {
    opacity: 0.38;
}

.calendar-day > * {
    position: relative;
    z-index: 1;
}

.calendar-day:hover,
.calendar-day:focus-visible {
    background: color-mix(in srgb, var(--accent) 9%, transparent);
    outline: none;
}

.calendar-day:hover::after {
    opacity: 0.8;
}

/* Today is circled in pen rather than filled with a solid block: the
   filled tile was the only hard-edged shape left on the page. */
.calendar-day--today {
    background-color: transparent;
    color: var(--foreground);
    font-weight: 700;
    box-shadow: none;
    border-color: transparent;
}

/* Circled instead of boxed — drawing both would double the outline. */
.calendar-day--today::after {
    content: none;
}

.calendar-day--today::before {
    content: "";
    position: absolute;
    inset: 6%;
    z-index: 0;
    pointer-events: none;
    background-repeat: no-repeat;
    background-size: 100% 100%;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 120' preserveAspectRatio='none'%3E%3Cpath d='M62 8C31 9 10 30 11 60c1 29 22 50 51 51 28 1 48-21 47-52C108 29 90 7 62 8' fill='none' stroke='%230b7a55' stroke-width='3' stroke-linecap='round' opacity='.85'/%3E%3Cpath d='M56 13C29 16 15 34 16 60c1 25 17 43 42 46' fill='none' stroke='%230b7a55' stroke-width='2.2' stroke-linecap='round' opacity='.5'/%3E%3C/svg%3E");
}

.calendar-day--today > * {
    position: relative;
    z-index: 1;
}

.calendar-day--today:hover {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
}

/* Badges: inked dots, not filled pills. */
.calendar-status-badge {
    border-width: 1.5px;
    border-radius: 60% 40% 55% 45% / 45% 55% 40% 60%;
    background: transparent;
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-size: 0.8rem;
    font-weight: 700;
}

.calendar-status-badge--weight {
    border-color: color-mix(in srgb, var(--accent) 70%, transparent);
    color: var(--accent-ink, var(--accent));
}

.calendar-status-badge--logged {
    border-color: #2a7047;
    color: #2a7047;
}

.calendar-status-badge--due {
    border-color: #96590c;
    color: #96590c;
}

/* The circled day keeps its own badge colours; the old rule washed them
   white to sit on the solid accent fill that is now gone. */
.calendar-day--today .calendar-status-badge {
    background: transparent;
}

.calendar-day--today .calendar-status-badge--weight { border-color: color-mix(in srgb, var(--accent) 70%, transparent); }
.calendar-day--today .calendar-status-badge--logged { border-color: #2a7047; }
.calendar-day--today .calendar-status-badge--due { border-color: #96590c; }

/* ---------- Calendar week view ----------
   Ruled lines, not boxes. A box outline is authored in a roughly square
   viewBox, and a week row is about 800x60 — stretching the path that far
   flattens the wobble, drags the corners out of place and drove the
   outline straight through the date column. A horizontal rule only
   stretches along its own axis, so it keeps its hand at any width. */

.calendar-week-row {
    position: relative;
    border-radius: 0;
}

.calendar-week-row::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 5px;
    z-index: 0;
    pointer-events: none;
    opacity: 0.45;
    background-repeat: repeat-x;
    background-size: 120px 5px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 5'%3E%3Cpath d='M0 3.2c14-1.4 28 1.1 42 .3s28-2 42-1.1 24 2.4 36 1.3' fill='none' stroke='%231b2a22' stroke-width='1.3' stroke-linecap='round'/%3E%3C/svg%3E");
}

.calendar-week-row > * {
    position: relative;
    z-index: 1;
}

.calendar-week-row:hover {
    background: color-mix(in srgb, var(--accent) 7%, transparent);
}

.calendar-week-row:hover::after {
    opacity: 0.8;
}

/* Today is marked the way you would mark it on paper: highlighter across
   the row and a firmer line under it. */
.calendar-week-row--today {
    background: linear-gradient(
        180deg,
        transparent 8%,
        color-mix(in srgb, var(--accent) 17%, transparent) 8%,
        color-mix(in srgb, var(--accent) 17%, transparent) 92%,
        transparent 92%
    );
}

.calendar-week-row--today::after {
    opacity: 0.9;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 5'%3E%3Cpath d='M0 3.2c14-1.4 28 1.1 42 .3s28-2 42-1.1 24 2.4 36 1.3' fill='none' stroke='%230b7a55' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E");
}

/* The date column needs its own room: handwriting at two sizes was
   crowding inside a 3rem box. */
.calendar-week-date {
    width: 3.25rem;
    flex: 0 0 auto;
    text-align: center;
    line-height: 1;
}

.calendar-week-dow {
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-weight: 700;
    font-size: 1rem;
    line-height: 1;
    color: var(--muted-foreground);
}

.calendar-week-num {
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-weight: 700;
    font-size: 1.5rem;
    line-height: 1.15;
    color: var(--foreground);
}

.calendar-week-row--today .calendar-week-num,
.calendar-week-row--today .calendar-week-dow {
    color: var(--accent-dark, var(--accent));
}

/* ---------- Popups: one width, one voice ----------
   Modals were a mix of max-w-sm and max-w-md, so the same dialog felt
   like a different component depending on where it opened. */

/* Prompt and confirm dialogs mark their panel role="document" and are
   meant to stay small — a number pad does not want 44rem. */
.modal-backdrop .lg-panel:not([role="document"]),
.modal-backdrop .modal-sheet:not([role="document"]) {
    max-width: 44rem;
}

/* Chips inside popups name a choice, so they are handwritten like every
   other label rather than small typeset UI text. Keyed off the data
   attribute because the chips carry only utility classes. */
.modal-backdrop [data-log-date-shortcut],
.journal-app [data-log-date-shortcut] {
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-weight: 700;
    font-size: 1.05rem;
    border-radius: 12px 5px 11px 5px / 5px 11px 5px 12px;
}

.modal-section-title {
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-weight: 700;
    font-size: 1.25rem;
    line-height: 1.15;
    color: var(--foreground);
}

/* Card and row titles: the same handwritten voice as the Schedule tiles,
   so a tile does not change hand depending on which page it sits on. */
.lg-card-title,
.more-options-title {
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-weight: 700;
    font-size: 1.3rem;
    line-height: 1.1;
    color: var(--foreground);
}

/* Weight change reads as gain or loss without importing a second green
   that competes with the accent numerals beside it. */
.stat-change--good,
.stat-change--bad {
    color: inherit;
}

/* ---------- Calculator toggles ----------
   calculator.js toggles bg-green-500/bg-accent/text-white on these at
   runtime, so the look is defined here instead of in the markup. */

.calc-mode-btn,
.dose-unit-btn {
    border: 2px solid color-mix(in srgb, var(--foreground) 45%, transparent);
    border-radius: 14px 5px 12px 6px / 6px 12px 5px 14px;
    background: transparent;
    color: var(--muted-foreground);
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-weight: 700;
    font-size: 1.1rem;
    line-height: 1.1;
    transition: background-color 140ms ease, color 140ms ease, transform 140ms ease;
}

.calc-mode-btn:hover,
.dose-unit-btn:hover {
    color: var(--foreground);
    border-color: var(--foreground);
}

.calc-mode-btn.bg-green-500,
.calc-mode-btn.bg-accent,
.dose-unit-btn.bg-green-500,
.dose-unit-btn.bg-accent {
    border-color: var(--foreground);
    background: var(--accent-ink, var(--accent)) !important;
    color: #fff;
    box-shadow: 2px 3px 0 color-mix(in srgb, var(--foreground) 22%, transparent);
}

/* ---------- The list-and-figure standard ----------
   Rows and stat cards were each inventing their own voice, so the same
   kind of information looked different in every popup. One rule set:

     NAME of a row, item or figure   -> handwritten (.lg-card-title,
                                        .lg-stat-caption)
     ANNOTATION under it             -> typeset, small, muted
                                        (.lg-stat-note) — this is the
                                        "per mL" / "on syringe" voice
     MEASURED VALUE                  -> mono ink (.font-data)
     STATUS                          -> one chip (.lg-chip), with warn
                                        and danger variants reserved for
                                        real signals

   The App & Account popup was already right; everything else is brought
   to it rather than the other way round. */

.lg-stat-note:not([class*="flex"]):not([class*="grid"]):not([class*="inline"]):not(tr):not(th):not(td) {
    display: block;
}

.lg-stat-note {
    font-family: 'Figtree', ui-sans-serif, system-ui, sans-serif;
    font-weight: 400;
    font-size: 0.75rem;
    line-height: 1.25;
    letter-spacing: 0;
    text-transform: none;
    color: var(--muted-foreground);
}

/* ---------- Status chips ----------
   Hand-drawn tag rather than a filled pill, so a row of them reads as
   annotations on paper instead of UI badges. */

.lg-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.1rem 0.5rem;
    border: 1.5px solid color-mix(in srgb, var(--foreground) 30%, transparent);
    border-radius: 10px 4px 9px 4px / 4px 9px 4px 10px;
    background: transparent;
    color: var(--muted-foreground);
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-weight: 700;
    font-size: 1rem;
    line-height: 1.2;
    white-space: nowrap;
}

/* A measured value inside a chip stays mono — it is still a number. */
.lg-chip--value {
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-size: 0.72rem;
    font-weight: 500;
    color: var(--foreground);
}

.lg-chip--good {
    border-color: color-mix(in srgb, #2a7047 55%, transparent);
    color: #2a7047;
}

.lg-chip--warn {
    border-color: color-mix(in srgb, #96590c 60%, transparent);
    color: #96590c;
}

.lg-chip--danger {
    border-color: color-mix(in srgb, #b3382b 60%, transparent);
    color: #a3301f;
}

/* ---------- Colour swatches ----------
   Was 13 small circles each carrying a heavy black count bubble, which
   read as notification badges and crowded the row. Now they are inked
   dots — hand-drawn edge, ink outline — and the usage count is a quiet
   handwritten numeral that only appears when a colour is actually in
   use. The pen ring marks the selection. */

.lg-swatch {
    position: relative;
    width: 2.35rem;
    height: 2.35rem;
    flex: 0 0 auto;
    padding: 0;
    border: 2px solid var(--foreground);
    border-radius: 42% 58% 54% 46% / 54% 46% 58% 42%;
    cursor: pointer;
    overflow: visible;
    transition: transform 140ms cubic-bezier(0.16, 1, 0.3, 1), box-shadow 140ms ease;
}

.lg-swatch:hover {
    transform: translateY(-2px) rotate(-3deg);
    box-shadow: 2px 3px 0 color-mix(in srgb, var(--foreground) 25%, transparent);
}

/* selectColor() marks the active swatch with border-white; keep that hook
   and draw a pen ring around it instead of recolouring the border. */
.lg-swatch.border-white {
    border-color: var(--foreground) !important;
    transform: rotate(-4deg) scale(1.06);
    box-shadow:
        0 0 0 3px var(--card),
        0 0 0 5px var(--foreground),
        3px 4px 0 color-mix(in srgb, var(--foreground) 22%, transparent);
}

.lg-swatch .color-usage-count {
    position: absolute;
    right: -0.3rem;
    bottom: -0.45rem;
    z-index: 1;
    min-width: 0;
    height: auto;
    padding: 0 0.1rem;
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
    color: var(--foreground);
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-size: 0.95rem;
    font-weight: 700;
    line-height: 1;
}

/* JS adds this when the colour is unused, so an untouched palette is not
   a wall of zeroes. */
.lg-swatch .color-usage-count.is-empty {
    display: none;
}

/* ---------- Pencilled note ----------
   An aside inside a form: boxed in the accent rather than borrowing a
   blue "info" palette that appears nowhere else in the notebook. */

.lg-note {
    border: 1.5px solid color-mix(in srgb, var(--accent) 45%, transparent);
    border-radius: 14px 5px 12px 6px / 6px 12px 5px 14px;
    background: color-mix(in srgb, var(--accent) 7%, transparent);
}

/* One button height inside popups. Sizes were mixed per-button
   (--sm here, full size there), so a stack of actions in the same dialog
   stepped up and down. Ghost icon buttons are exempt — they are not part
   of that rhythm. */
.modal-backdrop .lg-btn:not(.lg-btn--ghost) {
    min-height: 2.5rem;
    padding-block: 0.4rem;
    font-size: 1.15rem;
}

/* ---------- Numbered step marker ----------
   A pen-circled numeral for how-to lists inside popups, instead of a
   solid filled disc. */

.lg-step {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 1.85rem;
    height: 1.85rem;
    flex: 0 0 auto;
    border: 2px solid var(--foreground);
    border-radius: 46% 54% 52% 48% / 52% 48% 54% 46%;
    background: transparent;
    color: var(--foreground);
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-weight: 700;
    font-size: 1.15rem;
    line-height: 1;
}

/* The kind of a changelog entry (Added / Fixed / Improved) names the
   change, so it is handwritten; the entry itself stays typeset. */
.lg-kind {
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-weight: 700;
    font-size: 1.1rem;
    color: var(--foreground);
}

.lg-kind::after {
    content: ":";
}

/* The onboarding picker uses its own class but is the same control, so
   it inherits the swatch treatment rather than keeping the old badge. */
.onboarding-color-option {
    position: relative;
    width: 2.35rem;
    height: 2.35rem;
    flex: 0 0 auto;
    padding: 0;
    border: 2px solid var(--foreground);
    border-radius: 42% 58% 54% 46% / 54% 46% 58% 42%;
    cursor: pointer;
    transition: transform 140ms cubic-bezier(0.16, 1, 0.3, 1), box-shadow 140ms ease;
}

.onboarding-color-option:hover {
    transform: translateY(-2px) rotate(-3deg);
    box-shadow: 2px 3px 0 color-mix(in srgb, var(--foreground) 25%, transparent);
}

.onboarding-color-option.border-white {
    border-color: var(--foreground) !important;
    transform: rotate(-4deg) scale(1.06);
    box-shadow:
        0 0 0 3px var(--card),
        0 0 0 5px var(--foreground),
        3px 4px 0 color-mix(in srgb, var(--foreground) 22%, transparent);
}

.onboarding-color-option .color-usage-count {
    position: absolute;
    right: -0.3rem;
    bottom: -0.45rem;
    min-width: 0;
    height: auto;
    padding: 0 0.1rem;
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
    color: var(--foreground);
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-size: 0.95rem;
    font-weight: 700;
    line-height: 1;
}

/* font-mono should mean the same face as .font-data inside the app. */
:where(.dashboard-app-shell, .journal-app) .font-mono {
    font-family: 'JetBrains Mono', ui-monospace, monospace;
}

/* ---------- Sidebar nav ----------
   The nav was the last piece of pre-redesign chrome: filled accent pill,
   ring, drop shadow. The active item is marked with the same highlighter
   sweep the page titles use, so where you are on the page and where you
   are in the nav are said the same way. */

.lg-nav__item {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 0.45rem 0.6rem;
    border-radius: 12px 4px 11px 5px / 5px 11px 4px 12px;
    color: var(--muted-foreground);
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-weight: 700;
    font-size: 1.3rem;
    line-height: 1.15;
    transition: color 140ms ease, transform 140ms cubic-bezier(0.16, 1, 0.3, 1);
}

.lg-nav__item svg {
    width: 1.05rem;
    height: 1.05rem;
    flex: 0 0 auto;
    stroke-width: 1.75;
}

.lg-nav__item:hover {
    color: var(--foreground);
    transform: translateX(2px);
}

.lg-nav__item--active {
    color: var(--foreground);
}

/* The same marker sweep as a page title, drawn behind the label only. */
.lg-nav__item--active .lg-nav__label {
    padding: 0 0.28em;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 44' preserveAspectRatio='none'%3E%3Cpath d='M6 27C13 13 34 10 62 11c30 1 76-5 108-2 15 1 24 6 22 15-2 10-40 12-92 12-38 0-84 3-92-2-6-3-6-9-2-7Z' fill='%230b7a55' opacity='.26'/%3E%3C/svg%3E");
    background-size: 100% 76%;
    background-position: 0 66%;
    background-repeat: no-repeat;
}

.lg-nav__item--active svg {
    color: var(--accent-ink, var(--accent));
}

/* The count badge beside Schedule is a drawn tag, like every other. */
.lg-nav__count {
    margin-left: auto;
    padding: 0 0.4rem;
    border: 1.5px solid color-mix(in srgb, var(--foreground) 30%, transparent);
    border-radius: 10px 4px 9px 4px / 4px 9px 4px 10px;
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-size: 0.7rem;
    font-weight: 500;
    color: var(--foreground);
}

/* ---------- Highlighter draws itself on ----------
   The marker sweeps across the title on load rather than being there
   from the first frame. */

@keyframes lgHighlightSweep {
    from { background-size: 0% 74%; }
    to   { background-size: 100% 74%; }
}

.lg-head--highlight .lg-head__title {
    animation: lgHighlightSweep 640ms cubic-bezier(0.4, 0, 0.3, 1) 120ms both;
}

@media (prefers-reduced-motion: reduce) {
    .lg-head--highlight .lg-head__title {
        animation: none;
    }
}

/* ---------- Page turn ----------
   Lifted from the pdf-flipbook widget in the tools project: a leaf
   rotating on rotateY about one edge inside a deep perspective, with a
   shadow that sweeps off as it settles. There the leaf is half a spread;
   here the whole page is the leaf.

   Direction matters — moving Home -> Schedule turns forward, and coming
   back turns the other way, so the nav order reads like the order of
   sheets in a notebook. An inline snippet in base.html compares this
   page's position with the last one and stamps data-turn before paint.

   Enter-only by design: animating the outgoing page would mean holding
   navigation back until it finished, which makes the app feel slower than
   it is. The incoming sheet carries the idea on its own. */

/* Two rules here are load-bearing for anything position: fixed inside a
   dashboard page — the modals, and until this was fixed, the bottom nav.

   A transformed element is the containing block for its fixed descendants,
   so `fill-mode: both` was the bug: it retained the final keyframe, and that
   keyframe still carries perspective(1900px), which is a non-identity matrix
   even at rotateY(0). The wrapper therefore stayed a containing block for
   the life of the page and the fixed bar scrolled with the content on iOS.
   `backwards` gives the same visual — the from-state applies before the
   animation starts — and leaves transform: none once it ends.

   Any persistent `will-change` hint also keeps this subtree on a compositor
   layer. On iOS that softens popup text, so the browser now promotes the
   short animation only while it is actually running. */
.page-enter {
    position: relative;
    backface-visibility: hidden;
    animation-duration: 560ms;
    animation-timing-function: cubic-bezier(0.2, 0.82, 0.22, 1);
    animation-fill-mode: backwards;
}

/* The page-turn wrapper is an opacity stacking context. Lift that context
   above persistent navigation whenever one of its popups is visible, so the
   popup backdrop actually covers the nav instead of being trapped below it. */
.page-enter:has(.modal-backdrop:not(.hidden)) {
    z-index: 90;
}

/* Forward: the next sheet swings in from the right, hinged at the spine
   on its left. */
.page-enter,
.page-enter[data-turn="forward"] {
    transform-origin: left center;
    animation-name: lgPageTurnForward;
}

/* Back: mirrored — hinged on the right, swinging in from the left. */
.page-enter[data-turn="back"] {
    transform-origin: right center;
    animation-name: lgPageTurnBack;
}

@keyframes lgPageTurnForward {
    from { transform: perspective(1900px) rotateY(-24deg) translateX(-1.5%) scale(0.985); opacity: 0; }
    45%  { opacity: 1; }
    to   { transform: perspective(1900px) rotateY(0deg) translateX(0) scale(1); opacity: 1; }
}

@keyframes lgPageTurnBack {
    from { transform: perspective(1900px) rotateY(24deg) translateX(1.5%) scale(0.985); opacity: 0; }
    45%  { opacity: 1; }
    to   { transform: perspective(1900px) rotateY(0deg) translateX(0) scale(1); opacity: 1; }
}

/* The shadow the turning leaf casts, cast from whichever edge is hinged. */
.page-enter::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    width: 40%;
    z-index: 60;
    pointer-events: none;
    animation: lgPageTurnShade 560ms cubic-bezier(0.2, 0.82, 0.22, 1) both;
}

.page-enter::before,
.page-enter[data-turn="forward"]::before {
    left: 0;
    background: linear-gradient(90deg,
        color-mix(in srgb, var(--foreground) 22%, transparent), transparent 70%);
}

.page-enter[data-turn="back"]::before {
    left: auto;
    right: 0;
    background: linear-gradient(270deg,
        color-mix(in srgb, var(--foreground) 22%, transparent), transparent 70%);
}

@keyframes lgPageTurnShade {
    from { opacity: 0.55; }
    to   { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .page-enter,
    .page-enter[data-turn="back"] {
        animation: none;
    }

    .page-enter::before {
        display: none;
    }
}

/* The More menu opens beside the nav rather than above it. */
.lg-nav__more {
    position: relative;
}

/* Doubled class so it outranks the .lg-menu base, which anchors to the
   bottom of its container. */
.lg-menu.lg-menu--nav {
    position: static;
    inset: auto;
    width: auto;
    margin: 0.35rem 0 0.15rem 0.55rem;
    box-shadow: 2px 3px 0 color-mix(in srgb, var(--foreground) 16%, transparent);
}

/* Slightly smaller than a nav item so the nesting reads. */
.lg-menu--nav .lg-menu__item {
    font-size: 1.15rem;
    padding: 0.35rem 0.5rem;
}

/* Popups centre on the content column, not the viewport: with a 260px
   sidebar, viewport-centring pushes every dialog noticeably to the left
   of the page it belongs to. */
@media (min-width: 1024px) {
    .journal-app .modal-backdrop {
        padding-left: 260px;
    }
}

/* ---------- One set of popup surfaces ----------
   Inside popups the same job was being done by five different tones:
   .modal-section-card (177 uses), bg-secondary/50 (39), bg-secondary (9),
   .modal-section-card-muted (1) and a handful of bg-card/NN. Two surfaces
   are enough — the sheet itself, and one recessed tone for the sections
   and rows sitting on it — so every popup reads the same way.

   Applied here rather than in 48 template spots, and scoped to popups so
   page-level uses of the same utilities are untouched. */

.modal-backdrop .modal-section-card,
.modal-backdrop .modal-section-card-muted {
    background: color-mix(in srgb, var(--secondary) 50%, var(--card));
    border-color: color-mix(in srgb, var(--border) 74%, transparent);
}

/* Rows sitting inside a section: the same tone, a touch deeper, so the
   nesting still reads without introducing a third colour. */
.modal-backdrop [class*="bg-secondary"]:not(input):not(select):not(textarea),
.modal-backdrop .modal-section-card [class*="bg-card"]:not(input):not(select):not(textarea) {
    background: color-mix(in srgb, var(--secondary) 72%, var(--card)) !important;
}

/* Marks More as something that opens rather than navigates, and turns
   over when the sub-list is showing. */
.lg-nav__chevron {
    width: 0.85rem;
    height: 0.85rem;
    flex: 0 0 auto;
    margin-left: auto;
    color: var(--muted-foreground);
    stroke-width: 2;
    transition: transform 180ms ease, color 140ms ease;
}

.lg-nav__item:hover .lg-nav__chevron {
    color: var(--foreground);
}

[aria-expanded="true"] .lg-nav__chevron {
    transform: rotate(180deg);
    color: var(--foreground);
}

/* ---------- Comparison table columns ----------
   Icon on one line, caption in a fixed band beneath it, so columns whose
   captions wrap to two lines still line up with the ones that do not. */

/* Auto table layout sized every column to its own content, so the icon
   row, the caption row and the value rows each found different centres.
   Fixed layout with one shared column width is the only way these line up. */
.compare-table {
    table-layout: fixed;
    /* Below this the four value columns get narrower than their own words
       and neighbours collide. The wrapper scrolls instead. */
    min-width: 40rem;
}

.compare-table th:first-child,
.compare-table td:first-child {
    width: 34%;
}

.compare-col {
    display: grid;
    grid-template-rows: auto auto;
    justify-items: center;
    align-content: start;
    gap: 0.5rem;
}

.compare-col__label {
    display: flex;
    align-items: flex-start;
    justify-content: center;
    min-height: 2.4em;
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-weight: 700;
    font-size: 1.2rem;
    line-height: 1.15;
    letter-spacing: 0;
    text-transform: none;
    color: var(--foreground);
}

/* Price and value cells: keep short values on one line so the row reads
   as a row rather than a ragged block. */
.compare-value {
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-size: 0.72rem;
    white-space: nowrap;
}

/* Multi-word values ("With desktop app") need the wrap that keeps a fixed
   column from overflowing; single values keep the nowrap. */
.compare-value--wrap {
    white-space: normal;
}

/* ---------- Daily check-in ----------
   The rating buttons and the on/off pill are the only controls whose fill is
   driven from JS: checkins.js swaps bg-primary/bg-secondary and the border
   colour utilities to show which value is picked. So these rules set shape,
   weight and rhythm only — no background, no colour, no border-colour — or
   unlayered CSS would beat those utilities and the active state would never
   show. */

.checkin-rating {
    border-width: 2px;
    border-style: solid;
    border-radius: 12px 5px 11px 6px / 6px 11px 5px 12px;
    padding-block: 0.45rem;
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-size: 0.9rem;
    font-weight: 700;
    transition: transform 140ms ease;
}

.checkin-rating:nth-child(even) {
    border-radius: 5px 12px 6px 11px / 11px 6px 12px 5px;
}

.checkin-rating:active {
    transform: translateY(1px);
}

.checkin-toggle {
    border-width: 2px;
    border-style: solid;
    border-radius: 999px;
    padding: 0.25rem 0.7rem;
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-size: 1rem;
    font-weight: 700;
    cursor: pointer;
    user-select: none;
}

/* ---------- Empty states ----------
   A note left in the space where the entries would be, in the same hand
   as the section titles rather than in UI grey. */

.lg-empty {
    text-align: center;
}

.lg-empty__title {
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-weight: 700;
    font-size: 1.9rem;
    line-height: 1.1;
    color: var(--foreground);
}

.lg-empty__sub {
    margin-top: 0.15rem;
    font-family: 'Figtree', ui-sans-serif, system-ui, sans-serif;
    font-size: 0.875rem;
    color: var(--muted-foreground);
}

/* ---------- User card + account menu ----------
   The card is a control, so it reads as one: it lifts slightly and shows
   a pen tick. The menu is a torn slip of paper pinned above it. */

.lg-user {
    position: relative;
}

.lg-user__card {
    display: block;
    width: 100%;
    text-align: left;
    cursor: pointer;
    transition: transform 140ms cubic-bezier(0.16, 1, 0.3, 1);
}

.lg-user__card:hover {
    transform: translate(-1px, -1px) rotate(-0.4deg);
}

.lg-user__chevron {
    flex: 0 0 auto;
    color: var(--muted-foreground);
    transition: transform 180ms ease, color 140ms ease;
}

.lg-user__card[aria-expanded="true"] .lg-user__chevron {
    transform: rotate(180deg);
    color: var(--foreground);
}

.lg-menu {
    position: absolute;
    bottom: calc(100% + 0.5rem);
    left: 0;
    right: 0;
    z-index: 60;
    padding: 0.4rem;
    border: 2px solid var(--foreground);
    border-radius: 14px 5px 12px 6px / 6px 12px 5px 14px;
    background: var(--card);
    box-shadow: 3px 4px 0 color-mix(in srgb, var(--foreground) 22%, transparent);
    transform-origin: bottom center;
}

.lg-menu[hidden] {
    display: none;
}

.lg-menu:not([hidden]) {
    animation: lgMenuIn 180ms cubic-bezier(0.16, 1, 0.3, 1) both;
}

@keyframes lgMenuIn {
    from { opacity: 0; transform: translateY(6px) rotate(-0.6deg); }
    to   { opacity: 1; transform: translateY(0) rotate(0); }
}

.lg-menu__item {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    width: 100%;
    padding: 0.45rem 0.55rem;
    border: 0;
    border-radius: 8px 3px 7px 3px / 3px 7px 3px 8px;
    background: transparent;
    color: var(--foreground);
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-weight: 700;
    font-size: 1.2rem;
    line-height: 1.1;
    text-align: left;
    cursor: pointer;
    transition: background-color 120ms ease;
}

.lg-menu__item:hover,
.lg-menu__item:focus-visible {
    background: color-mix(in srgb, var(--accent) 12%, transparent);
    outline: none;
}

.lg-menu__item svg {
    width: 1rem;
    height: 1rem;
    flex: 0 0 auto;
    color: var(--accent-ink, var(--accent));
    stroke-width: 1.75;
}

.lg-menu__item--danger {
    color: #b3382b;
}

.lg-menu__item--danger svg {
    color: #b3382b;
}

.lg-menu__item--danger:hover {
    background: color-mix(in srgb, #d4614f 14%, transparent);
}

/* A pen rule between the app items and the way out. */
.lg-menu__rule {
    height: 0;
    margin: 0.3rem 0.4rem;
    border: 0;
    border-top: 1.5px solid color-mix(in srgb, var(--foreground) 22%, transparent);
    border-radius: 50%;
}

/* ---------- Figures drawing themselves in ----------
   Rings, charts and bars start empty and fill on load, so the numbers
   look tallied rather than printed. figures.js drives the classes. */

.lg-ring-draw {
    transition: stroke-dasharray 1.15s cubic-bezier(0.22, 1, 0.36, 1);
}

/* Chart strokes ink in left-to-right, matching the margin doodles. */
.lg-draw {
    stroke-dasharray: 1;
    stroke-dashoffset: 1;
}

.lg-draw.is-drawn {
    stroke-dashoffset: 0;
    transition: stroke-dashoffset 1.5s cubic-bezier(0.42, 0, 0.3, 1);
}

/* Area fills and dots arrive after the line that defines them. */
.lg-figure-fade {
    opacity: 0;
}

.lg-figure-fade.is-shown {
    opacity: 1;
    transition: opacity 800ms ease 700ms;
}

.lg-bar-draw {
    transition: width 1s cubic-bezier(0.22, 1, 0.36, 1);
}

@media (prefers-reduced-motion: reduce) {
    .lg-ring-draw,
    .lg-draw,
    .lg-draw.is-drawn,
    .lg-figure-fade,
    .lg-figure-fade.is-shown,
    .lg-bar-draw,
    .lg-menu:not([hidden]) {
        transition: none;
        animation: none;
    }

    .lg-draw {
        stroke-dasharray: none;
        stroke-dashoffset: 0;
    }

    .lg-figure-fade {
        opacity: 1;
    }
}

/* ---------- Tallied stats ----------
   Counts written out by hand and ringed with a pen, the way you'd keep a
   running total in a margin. */

.lg-stat {
    position: relative;
    text-align: center;
}

.lg-stat__value {
    position: relative;
    display: inline-block;
    padding: 0.35rem 0.75rem 0.5rem;
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-weight: 700;
    font-size: clamp(2.4rem, 4vw, 3.2rem);
    line-height: 1;
    color: var(--accent-ink, var(--accent));
}

/* The pen ring, drawn twice round like a real circling. */
.lg-stat__value::after {
    content: "";
    position: absolute;
    inset: -14% -10%;
    background-repeat: no-repeat;
    background-size: 100% 100%;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 70' preserveAspectRatio='none'%3E%3Cpath d='M62 6C34 5 10 17 9 34c-1 18 26 30 55 30 27 0 47-12 47-28C111 19 90 7 62 6' fill='none' stroke='%230b7a55' stroke-width='2.4' stroke-linecap='round' opacity='.55'/%3E%3Cpath d='M58 9C31 10 13 21 13 36c0 16 22 26 48 25' fill='none' stroke='%230b7a55' stroke-width='1.8' stroke-linecap='round' opacity='.4'/%3E%3C/svg%3E");
    pointer-events: none;
}

.lg-stat__label {
    display: block;
    margin-top: 0.5rem;
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-size: 1.1rem;
    color: var(--muted-foreground);
    transform: rotate(-1.5deg);
}

/* Wobbly hand-drawn divider between tallies. */
.lg-stat + .lg-stat::before {
    content: "";
    position: absolute;
    left: -0.5rem;
    top: 12%;
    bottom: 12%;
    width: 2px;
    background-repeat: no-repeat;
    background-size: 100% 100%;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 6 100' preserveAspectRatio='none'%3E%3Cpath d='M3 2c-2 20 3 30 0 48-2 16 2 30 0 48' fill='none' stroke='%231b2a22' stroke-width='2' stroke-linecap='round' opacity='.22'/%3E%3C/svg%3E");
}

@media (max-width: 639px) {
    .lg-stat + .lg-stat::before {
        display: none;
    }
}

/* ---------- Crossed-out price ----------
   A price struck through by hand with the real answer written beside it.
   The strike draws itself when scrolled to. */

.lg-struck {
    position: relative;
    display: inline-block;
    color: var(--muted-foreground);
}

.lg-struck::after {
    content: "";
    position: absolute;
    left: -6%;
    right: -6%;
    top: 45%;
    height: 0.55em;
    background-repeat: no-repeat;
    background-size: 100% 100%;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 20' preserveAspectRatio='none'%3E%3Cpath d='M4 15C46 6 92 14 134 7c22-4 44 2 62-3' fill='none' stroke='%23b91c1c' stroke-width='4' stroke-linecap='round'/%3E%3C/svg%3E");
    transform: scaleX(0);
    transform-origin: left center;
}

[data-ink].is-drawn .lg-struck::after,
.lg-struck[data-ink].is-drawn::after {
    transform: scaleX(1);
    transition: transform 480ms cubic-bezier(0.5, 0, 0.4, 1);
}

/* The replacement, written in after the strike lands. */
.lg-struck-answer {
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-weight: 700;
    color: var(--accent-ink, var(--accent));
    display: inline-block;
    opacity: 0;
    transform: rotate(-7deg) scale(0.7);
}

[data-ink].is-drawn .lg-struck-answer {
    opacity: 1;
    transform: rotate(-7deg) scale(1);
    transition:
        opacity 260ms ease 420ms,
        transform 420ms cubic-bezier(0.2, 1.5, 0.4, 1) 420ms;
}

/* ---------- Steps: drawn 1 → 2 → 3 ---------- */

[data-steps] .lg-step-line {
    transform: scaleX(0);
    transform-origin: left center;
}

[data-steps].is-drawn .lg-step-line {
    transform: scaleX(1);
    transition: transform 1.5s cubic-bezier(0.5, 0, 0.3, 1) 0.2s;
}

[data-steps] .lg-step-num {
    opacity: 0;
    transform: scale(0.5) rotate(-12deg);
}

[data-steps].is-drawn .lg-step-num {
    opacity: 1;
    transform: scale(1) rotate(0deg);
    transition:
        opacity 240ms ease,
        transform 460ms cubic-bezier(0.2, 1.6, 0.4, 1);
}

/* Land each number as the line reaches it. */
[data-steps].is-drawn > .grid > *:nth-child(1) .lg-step-num { transition-delay: 0.15s; }
[data-steps].is-drawn > .grid > *:nth-child(2) .lg-step-num { transition-delay: 0.75s; }
[data-steps].is-drawn > .grid > *:nth-child(3) .lg-step-num { transition-delay: 1.35s; }

@media (prefers-reduced-motion: reduce) {
    [data-steps] .lg-step-line,
    [data-steps] .lg-step-num,
    .lg-struck::after,
    .lg-struck-answer {
        opacity: 1;
        transform: none;
        transition: none;
    }

    .lg-struck::after {
        transform: scaleX(1);
    }
}

/* ---------- Steps ---------- */

.lg-step-num {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 3rem;
    height: 3rem;
    margin: 0 auto 1rem;
    border-radius: 0.625rem;
    border: 1px solid var(--accent);
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    color: var(--accent-ink, var(--accent));
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-weight: 700;
    font-size: 1.05rem;
}

.lg-step-line {
    position: absolute;
    top: 1.5rem;
    left: 16.67%;
    right: 16.67%;
    height: 2px;
    background-image: repeating-linear-gradient(
        to right,
        color-mix(in srgb, var(--accent) 55%, transparent) 0 2px,
        transparent 2px 0.5rem);
}

/* ---------- Disclosure ---------- */

.lg-faq summary {
    list-style: none;
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-weight: 700;
    font-size: 1.4rem;
    line-height: 1.15;
    letter-spacing: 0;
    color: var(--foreground);
}

.lg-faq summary > span {
    font-family: inherit;
}

/* Answers stay typeset — handwriting is for the question, not the prose. */
.lg-faq > p,
.lg-faq > div p {
    font-family: 'Figtree', ui-sans-serif, system-ui, sans-serif;
}

.lg-faq summary::-webkit-details-marker {
    display: none;
}

.lg-faq[open] .lg-faq-chevron {
    transform: rotate(180deg);
}

.lg-faq-chevron {
    transition: transform 220ms cubic-bezier(0.16, 1, 0.3, 1);
}

/* ---------- Today's doses: taped notebook rows ----------
   These used to be the last generic SaaS cards on the dashboard: a grey
   pill, tinted icon slab, and soft green button. Keep the peptide colour as
   an ink annotation while the row itself uses the same paper, pen, tape, and
   hard-shadow language as the rest of the journal. */

.upcoming-card {
    position: relative;
    isolation: isolate;
    min-height: 5.6rem;
    display: flex;
    align-items: center;
    gap: 0.95rem;
    padding: 1rem 1rem 1rem 1.2rem;
    overflow: visible;
    border: 2px solid var(--foreground);
    border-radius: 255px 15px 225px 14px / 14px 225px 15px 255px;
    background: var(--card);
    box-shadow: 3px 4px 0 color-mix(in srgb, var(--foreground) 20%, transparent);
    transition: transform 140ms cubic-bezier(0.16, 1, 0.3, 1), box-shadow 140ms ease, background-color 140ms ease;
}

.upcoming-card::before {
    content: "";
    position: absolute;
    z-index: -1;
    left: 0.55rem;
    top: 0.8rem;
    bottom: 0.8rem;
    width: 0.28rem;
    border-radius: 55% 45% 60% 40% / 40% 60% 45% 55%;
    background: var(--upcoming-accent, var(--accent));
    opacity: 0.78;
    transform: rotate(-1.5deg);
}

.upcoming-card::after {
    content: "";
    position: absolute;
    z-index: 3;
    top: -0.65rem;
    right: 13%;
    width: 3.8rem;
    height: 1.15rem;
    pointer-events: none;
    background: linear-gradient(180deg, color-mix(in srgb, #fffdf2 72%, transparent), color-mix(in srgb, #ebe7d2 62%, transparent));
    box-shadow: 0 1px 2px color-mix(in srgb, var(--foreground) 12%, transparent);
    clip-path: polygon(2% 8%, 8% 0%, 100% 5%, 96% 94%, 6% 100%, 0% 88%);
    transform: rotate(2.2deg);
}

#upcoming-container > .upcoming-card:nth-child(even) {
    border-radius: 18px 255px 15px 225px / 225px 15px 255px 14px;
}

#upcoming-container > .upcoming-card:nth-child(even)::after {
    right: auto;
    left: 58%;
    transform: rotate(-2.8deg);
}

.upcoming-card:hover,
.upcoming-card:focus-within {
    border-color: var(--foreground);
    background: var(--card-hover);
    box-shadow: 5px 6px 0 color-mix(in srgb, var(--foreground) 27%, transparent);
    transform: translate(-1px, -2px) rotate(-0.18deg);
}

.upcoming-card-icon {
    width: 3rem;
    height: 3rem;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    border: 2px solid color-mix(in srgb, var(--upcoming-accent, var(--accent)) 72%, var(--foreground));
    border-radius: 42% 58% 54% 46% / 54% 46% 58% 42%;
    background: color-mix(in srgb, var(--upcoming-accent, var(--accent)) 9%, var(--card));
    box-shadow: 2px 2px 0 color-mix(in srgb, var(--upcoming-accent, var(--accent)) 24%, transparent);
    color: color-mix(in srgb, var(--upcoming-accent, var(--accent)) 82%, var(--foreground));
    transform: rotate(-2deg);
}

.upcoming-card-icon svg {
    stroke-linecap: round;
    stroke-linejoin: round;
}

.upcoming-card-body {
    min-width: 0;
    flex: 1 1 auto;
}

.upcoming-card-heading {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.35rem 0.65rem;
}

.upcoming-card-title {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-size: 1.55rem;
    font-weight: 700;
    line-height: 1;
    color: var(--foreground);
}

.upcoming-card-detail {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.2rem 0.45rem;
    margin-top: 0.42rem;
    font-family: 'Figtree', ui-sans-serif, system-ui, sans-serif;
    font-size: 0.82rem;
    line-height: 1.3;
    color: var(--muted-foreground);
}

.upcoming-card-detail .font-data {
    color: var(--foreground);
    font-size: 0.78rem;
    font-weight: 600;
}

.upcoming-card-separator {
    color: color-mix(in srgb, var(--upcoming-accent, var(--accent)) 72%, var(--muted-foreground));
    font-weight: 700;
}

.upcoming-card-meta {
    margin-top: 0.35rem;
    font-family: 'Figtree', ui-sans-serif, system-ui, sans-serif;
    font-size: 0.72rem;
    line-height: 1.25;
    color: color-mix(in srgb, var(--muted-foreground) 82%, transparent);
}

.upcoming-card .status-badge-due {
    border-color: color-mix(in srgb, #96590c 58%, transparent);
    background: transparent;
    box-shadow: none;
    color: #96590c;
}

.upcoming-log-button {
    min-width: 4.15rem;
    min-height: 2.75rem;
    margin-left: auto;
    padding: 0.45rem 1rem 0.5rem;
    flex: 0 0 auto;
    border: 2px solid var(--foreground);
    border-radius: 14px 5px 12px 6px / 6px 12px 5px 14px;
    background: var(--accent-ink, var(--accent));
    box-shadow: 2px 3px 0 color-mix(in srgb, var(--foreground) 28%, transparent);
    color: #fff;
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-size: 1.25rem;
    font-weight: 700;
    line-height: 1;
    transition: transform 140ms cubic-bezier(0.16, 1, 0.3, 1), box-shadow 140ms ease, background-color 140ms ease;
}

.upcoming-log-button:hover,
.upcoming-log-button:focus-visible {
    background: var(--accent-dark, var(--accent-ink, var(--accent)));
    box-shadow: 4px 5px 0 color-mix(in srgb, var(--foreground) 30%, transparent);
    outline: none;
    transform: translate(-1px, -2px) rotate(0.5deg);
}

.upcoming-log-button:focus-visible {
    outline: 3px solid color-mix(in srgb, var(--accent) 32%, transparent);
    outline-offset: 3px;
}

.upcoming-log-button:active {
    box-shadow: none;
    transform: translate(1px, 1px);
}

@media (max-width: 479px) {
    .upcoming-card {
        gap: 0.7rem;
        padding: 0.9rem 0.75rem 0.9rem 1rem;
    }

    .upcoming-card-icon {
        width: 2.65rem;
        height: 2.65rem;
    }

    .upcoming-card-title {
        font-size: 1.4rem;
    }

    .upcoming-card-detail {
        gap-inline: 0.3rem;
        font-size: 0.75rem;
    }

    .upcoming-log-button {
        min-width: 3.65rem;
        padding-inline: 0.72rem;
    }
}

/* ---------- Logged today: completed ledger rows ----------
   Logged doses are the finished entries beneath the taped due cards. They
   use the same paper-and-ink system, but stay untaped and slightly quieter so
   completion reads as a settled line in the notebook rather than another
   action card. */

.today-logs-heading {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    margin-bottom: 0.85rem;
}

.today-logs-heading-check {
    width: 1.35rem;
    height: 1.35rem;
    flex: 0 0 auto;
    color: #4f8125;
    transform: rotate(-5deg);
}

.today-log-card {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.85rem;
    min-height: 4.9rem;
    padding: 0.85rem 1rem;
    border: 2px solid color-mix(in srgb, var(--foreground) 84%, transparent);
    border-radius: 225px 13px 255px 15px / 14px 255px 13px 225px;
    background: color-mix(in srgb, var(--card) 94%, var(--accent) 6%);
    box-shadow: 2px 3px 0 color-mix(in srgb, var(--foreground) 15%, transparent);
}

.today-log-card:nth-child(even) {
    border-radius: 15px 255px 13px 225px / 225px 14px 255px 13px;
}

/* A faint ruled stroke keeps a completed entry feeling written into the
   notebook rather than dropped onto it as another rounded component. */
.today-log-card::after {
    content: "";
    position: absolute;
    left: 1rem;
    right: 1rem;
    bottom: 0.35rem;
    height: 3px;
    pointer-events: none;
    opacity: 0.18;
    background-repeat: repeat-x;
    background-size: 120px 3px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 3'%3E%3Cpath d='M0 2c14-1 28 .8 42 .2s28-1.4 42-.6 24 1.6 36 .8' fill='none' stroke='%231b2a22' stroke-width='1.1' stroke-linecap='round'/%3E%3C/svg%3E");
}

.today-log-dot {
    width: 0.95rem;
    height: 0.95rem;
    flex: 0 0 auto;
    border: 1.5px solid color-mix(in srgb, var(--foreground) 70%, transparent);
    border-radius: 43% 57% 52% 48% / 55% 45% 58% 42%;
    background: var(--today-log-accent, var(--accent));
    box-shadow: 1px 1px 0 color-mix(in srgb, var(--foreground) 14%, transparent);
    transform: rotate(-4deg);
}

.today-log-body {
    min-width: 0;
    flex: 1 1 auto;
}

.today-log-title {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-family: 'Caveat', ui-sans-serif, cursive;
    font-size: 1.45rem;
    font-weight: 700;
    line-height: 1;
    color: var(--foreground);
}

.today-log-detail {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.18rem 0.42rem;
    margin-top: 0.38rem;
    font-family: 'Figtree', ui-sans-serif, system-ui, sans-serif;
    font-size: 0.78rem;
    line-height: 1.3;
    color: var(--muted-foreground);
}

.today-log-detail .font-data {
    color: var(--foreground);
    font-size: 0.76rem;
    font-weight: 600;
}

.today-log-separator {
    color: color-mix(in srgb, var(--today-log-accent, var(--accent)) 68%, var(--muted-foreground));
    font-weight: 700;
}

.today-log-check {
    width: 1.75rem;
    height: 1.75rem;
    flex: 0 0 auto;
    padding: 0.15rem;
    border-radius: 48% 52% 44% 56% / 55% 45% 58% 42%;
    color: #4f8125;
    stroke-width: 2.5;
    transform: rotate(-4deg);
}

@media (max-width: 479px) {
    .today-log-card {
        min-height: 4.65rem;
        gap: 0.7rem;
        padding: 0.8rem 0.85rem;
    }

    .today-log-title {
        font-size: 1.35rem;
    }

    .today-log-detail {
        gap-inline: 0.3rem;
        font-size: 0.74rem;
    }

    .today-log-check {
        width: 1.55rem;
        height: 1.55rem;
    }
}

/* ---------- Motion ---------- */

@media (prefers-reduced-motion: reduce) {
    .lg-btn,
    .lg-tile,
    .lg-tile::before {
        transition: none;
    }

    .lg-measure::before {
        animation: none;
    }
}
