/**
 * TrackIt component layer.
 *
 * Semantic classes built entirely on the tokens in tokens.css. Purely additive:
 * nothing here overrides a Tailwind utility, so existing markup keeps working
 * and call sites can migrate one component at a time.
 *
 * The rule that keeps this stable: components read tokens, never raw hex, and
 * never redefine a utility class. Restyling happens in tokens.css alone.
 */

/* ====================================================================== */
/* Buttons                                                                 */
/* ====================================================================== */

.tk-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--tk-space-2);
    padding: 0.625rem 1.125rem;
    border: 1px solid transparent;
    border-radius: var(--tk-radius);
    font-family: var(--tk-font);
    font-size: var(--tk-text-sm);
    font-weight: 600;
    line-height: 1.2;
    cursor: pointer;
    text-decoration: none;
    transition: background-color var(--tk-duration) var(--tk-ease),
                border-color var(--tk-duration) var(--tk-ease),
                transform var(--tk-duration) var(--tk-ease);
    /* 44px minimum touch target -- this is a PWA used one-handed at the gym. */
    min-height: 44px;
}

.tk-btn:active { transform: translateY(1px); }

.tk-btn:focus-visible {
    outline: 2px solid var(--tk-accent);
    outline-offset: 2px;
}

.tk-btn[disabled],
.tk-btn[aria-disabled="true"] {
    opacity: 0.5;
    cursor: not-allowed;
    transform: none;
}

/* A primary is near-white ink on the dark theme, and half of that is a solid
   mid-grey -- brighter and more button-looking than several enabled controls
   beside it. A muted fill says unavailable outright, and the label stays
   legible on it in both themes. */
.tk-btn--primary[disabled],
.tk-btn--primary[aria-disabled="true"] {
    opacity: 1;
    background-color: var(--tk-ink-muted);
    border-color: var(--tk-ink-muted);
}

.tk-btn--primary {
    background-color: var(--tk-action);
    color: var(--tk-action-ink);
}
.tk-btn--primary:hover:not([disabled]) { background-color: var(--tk-action-hover); }
.tk-btn--block { width: 100%; }

/* ====================================================================== */
/* Surfaces                                                                */
/* ====================================================================== */

.tk-card {
    background-color: var(--tk-surface);
    border: 1px solid var(--tk-border);
    border-radius: var(--tk-radius-lg);
    box-shadow: var(--tk-shadow);
    padding: var(--tk-space-5);
}

/* ====================================================================== */
/* Progress fills — applied to the existing #*-progress bars               */
/* ====================================================================== */

/* These replace the Tailwind colour utilities that were on the dashboard
   bars (protein=bg-red-500, carbs=bg-green-500, fat=bg-yellow-500). That
   combination failed validation outright: carbs-green against fat-yellow
   measured dE 4.2 for a protanope, so roughly one man in twelve could not tell
   the two bars apart. The token hues are validated all-pairs in both modes.

   Only the colour utility is replaced -- the sizing, rounding and
   .progress-bar transition classes stay on the element, and the JS that drives
   these bars only ever sets style.width, so nothing here affects behaviour. */
.tk-track { background-color: var(--tk-surface-sunken); }

.tk-fill-protein { background-color: var(--tk-protein); }
.tk-fill-carbs   { background-color: var(--tk-carbs); }
.tk-fill-fat     { background-color: var(--tk-fat); }
.tk-fill-accent  { background-color: var(--tk-accent); }

/* ====================================================================== */
/* Pro lock                                                               */
/* ====================================================================== */

/* The paywall is the one screen where the product is asking for money, and it
   arrived as a hard cut with a generic line of copy. A short staggered reveal
   -- mark, then what it does, then the price -- reads as composed rather than
   as a shutter coming down, and lets someone finish reading the sentence
   before the button asks for a decision.

   Built on the motion tokens, so the reduced-motion sweep in tokens.css
   already collapses it to nothing. */
@keyframes tk-prolock-rise {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
}

.tk-prolock__icon,
.tk-prolock__copy,
.tk-prolock__action {
    animation: tk-prolock-rise calc(var(--tk-duration) * 2) var(--tk-ease) both;
}

.tk-prolock__copy   { animation-delay: calc(var(--tk-duration) * 0.5); }
.tk-prolock__action { animation-delay: var(--tk-duration); }

/* The lock mark settles a beat after it lands, so the eye goes to it first. */
.tk-prolock__icon {
    animation:
        tk-prolock-rise calc(var(--tk-duration) * 2) var(--tk-ease) both,
        tk-prolock-settle calc(var(--tk-duration) * 3) var(--tk-ease) calc(var(--tk-duration) * 2) both;
}

@keyframes tk-prolock-settle {
    0%   { transform: scale(1); }
    45%  { transform: scale(1.06); }
    100% { transform: scale(1); }
}

/* ====================================================================== */
/* Touch targets                                                          */
/* ====================================================================== */

/* 44px square is the accessibility floor, and this is a PWA people use on a
   phone in a kitchen or a gym. Twenty controls sat under it; the disclosure row
   for micronutrients was 16px tall, which is a coin-flip to hit.

   Done as min-height rather than a ::after hit-area overlay because several of
   these already use pseudo-elements, and a control that looks the size it
   actually is beats an invisible one that does not. */
#profile-avatar-btn,
#drawer-close-btn {
    min-width: 44px;
    min-height: 44px;
}

#today-btn,
#date-picker,
.catalogue-filter-btn.tk-segment {
    min-height: 44px;
}

/* Menu rows: `block` with padding leaves the label at the top once a minimum
   height applies, so these centre it instead. */
#profile-settings-btn,
#dropdown-settings-btn,
#dropdown-logout-btn {
    display: flex;
    align-items: center;
    min-height: 44px;
}

/* Already a flex row, so it only needs the height. */
#dashboard-micros-toggle {
    min-height: 44px;
}

/* A brand asset at a fixed 120x17, so the artwork stays put and the padding
   does the work. */
.tk-attribution {
    display: inline-block;
    padding: 14px 8px;
}

/* ====================================================================== */
/* Swipe to delete                                                        */
/* ====================================================================== */

/* The row slides over a red ground that is revealed rather than animated in,
   so the meaning is visible the moment the finger moves instead of arriving
   after a threshold. */
.food-log-item {
    position: relative;
    touch-action: pan-y;
    transition: transform var(--tk-duration) var(--tk-ease);
    will-change: transform;
}

/* The ground is the row's own pseudo-element, sitting past its right edge, so
   only an ancestor can stop it. Unclipped it painted a viewport-wide band that
   was already visible in the card's padding at rest and ran to the edge of the
   screen on a swipe. The list clips it to the row's own width, which is also
   the widest strip the gesture can ever uncover. */
#food-log {
    overflow: hidden;
}

/* Scoped to the swipeable list. History rows reuse .food-log-item but are not
   swipeable, so there the band was pure artifact -- a red edge down a modal
   that no gesture could explain. */
#food-log .food-log-item::after {
    content: "Delete";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 100%;
    width: 100%;
    display: flex;
    align-items: center;
    padding-left: var(--tk-space-4);
    font-size: var(--tk-text-xs);
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: #fff;
    background-color: var(--tk-critical);
    opacity: 0.55;
    transition: opacity var(--tk-duration) var(--tk-ease);
}

/* Past the commit point, so the change in weight is the signal that letting go
   will actually delete. */
#food-log .food-log-item.is-deleting::after { opacity: 1; }

/* ---- Log rows ------------------------------------------------------------ */
/* A divided list, not a stack of outlined boxes. Name on one line, the amount
   and the three macros under it in small type, calories on the right; tap
   the row for the micronutrients and the actions. */
#food-log, #presets-list, #saved-recipes-list { display: block; }
#food-log > li + li, #presets-list > li + li, #saved-recipes-list > li + li { margin-top: 0; }
.tk-logrow {
    background-color: var(--tk-surface);
    border: 0;
    border-bottom: 1px solid var(--tk-border);
    border-radius: 0;
    box-shadow: none;
    padding: 0;
}
.tk-logrow:last-child { border-bottom: 0; }
.tk-logrow__main {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--tk-space-3);
    width: 100%;
    min-height: 56px;
    padding: var(--tk-space-2) var(--tk-space-1);
    border: 0;
    background: none;
    text-align: left;
    font-family: var(--tk-font);
    color: var(--tk-ink);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}
.tk-logrow__text { display: grid; gap: 3px; min-width: 0; }
.tk-logrow__name {
    font-size: var(--tk-text-base);
    font-weight: 600;
    line-height: 1.25;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.tk-logrow__sub {
    display: flex;
    align-items: center;
    gap: var(--tk-space-2);
    font-size: var(--tk-text-xs);
    color: var(--tk-ink-muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    overflow: hidden;
}
.tk-logrow__amount { overflow: hidden; text-overflow: ellipsis; }
.tk-logrow__amount::after { content: "\00b7"; margin-left: var(--tk-space-2); color: var(--tk-border-strong); }
.tk-logrow__m { font-weight: 700; }
.tk-logrow__m--p { color: var(--tk-protein); }
.tk-logrow__m--c { color: var(--tk-carbs); }
.tk-logrow__m--f { color: var(--tk-fat); }
.tk-logrow__kcal {
    flex: 0 0 auto;
    font-size: var(--tk-text-base);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--tk-ink);
}
.tk-logrow__kcal small {
    margin-left: 2px;
    font-size: var(--tk-text-xs);
    font-weight: 500;
    color: var(--tk-ink-muted);
}
.tk-logrow__detail {
    padding: 0 var(--tk-space-1) var(--tk-space-3);
    animation: tk-row-enter calc(var(--tk-duration) * 2) var(--tk-ease) both;
}
.tk-logrow__micros {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: var(--tk-space-2);
    margin: 0 0 var(--tk-space-3);
    padding: var(--tk-space-2) var(--tk-space-3);
    border-radius: var(--tk-radius);
    background-color: var(--tk-surface-sunken);
}
.tk-logrow__micros dt {
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--tk-ink-muted);
}
.tk-logrow__micros dd {
    margin: 0;
    font-size: var(--tk-text-sm);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--tk-ink);
}
.tk-logrow__actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--tk-space-2);
}
.tk-logrow__source {
    margin-right: auto;
    font-size: var(--tk-text-xs);
    color: var(--tk-ink-muted);
}
/* A preset row: the tappable part and the Add button side by side. */
.tk-logrow__main--split { display: flex; align-items: center; gap: var(--tk-space-2); padding: 0; }
.tk-logrow__open {
    display: flex; align-items: center; justify-content: space-between; gap: var(--tk-space-3);
    flex: 1 1 auto; min-width: 0; min-height: 56px;
    padding: var(--tk-space-2) var(--tk-space-1);
    border: 0; background: none; text-align: left;
    font-family: var(--tk-font); color: var(--tk-ink); cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}
.tk-logrow__chev { flex: 0 0 auto; width: 18px; height: 18px; color: var(--tk-ink-muted); }
.tk-btn--sm { min-height: 36px; padding: 0.4rem 0.85rem; }

/* The history modal: the day's figures in a strip, then the same rows as
   Today's Log. It was three lines of "Protein: 64.9g" and boxed rows. */
.tk-hist {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: var(--tk-space-2);
    margin-bottom: var(--tk-space-3);
    padding: var(--tk-space-3) var(--tk-space-2);
    border-radius: var(--tk-radius);
    background-color: var(--tk-surface-sunken);
    text-align: center;
}
.tk-hist__figure { display: grid; gap: 2px; min-width: 0; }
.tk-hist__value { font-size: var(--tk-text-base); font-weight: 800; color: var(--tk-ink); font-variant-numeric: tabular-nums; }
.tk-hist__label { font-size: 10px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--tk-ink-muted); }
.tk-hist__list { display: block; }
.tk-hist__list > li + li { margin-top: 0; }
.tk-btn--secondary {
    background-color: var(--tk-surface-raised);
    border-color: var(--tk-border-strong);
    color: var(--tk-ink);
}
.tk-btn--danger { color: var(--tk-critical-ink); }

/* ====================================================================== */
/* Chart skeleton                                                         */
/* ====================================================================== */

/* .tk-skeleton and its shimmer were written and then never used anywhere --
   every wait in the app was a spinner. Bars in the shape of the chart that is
   coming read as the content loading rather than as the app thinking. */
.tk-chart-skeleton {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--tk-space-2);
    padding: var(--tk-space-2) 0;
}

.tk-chart-skeleton > span {
    flex: 1 1 0;
    display: block;
    min-width: 0;
}

/* ====================================================================== */
/* Connection state                                                       */
/* ====================================================================== */

/* Sits above the bottom nav rather than at the top, because that is where the
   thumb and the attention already are while logging. Announced politely via
   role=status, so it is read once rather than interrupting. */
.tk-connection {
    position: fixed;
    left: 50%;
    bottom: calc(72px + env(safe-area-inset-bottom, 0px));
    transform: translate(-50%, 8px);
    z-index: 70;
    max-width: calc(100vw - var(--tk-space-6));
    padding: var(--tk-space-2) var(--tk-space-4);
    border-radius: var(--tk-radius-full);
    font-size: var(--tk-text-xs);
    font-weight: 600;
    white-space: nowrap;
    color: var(--tk-ink-inverse);
    background-color: var(--tk-ink);
    box-shadow: var(--tk-shadow-lg);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--tk-duration) var(--tk-ease),
                transform var(--tk-duration) var(--tk-ease);
}

.tk-connection.is-visible {
    opacity: 1;
    transform: translate(-50%, 0);
}

.tk-connection[data-tone="offline"] { background-color: var(--tk-serious); color: #fff; }
.tk-connection[data-tone="online"]  { background-color: var(--tk-good); color: #fff; }

/* ====================================================================== */
/* Boot state                                                             */
/* ====================================================================== */

/* This was a bare line of grey text reading "Initializing App...", sitting at
   the top of an otherwise empty page. It is the first thing anyone sees, and
   on a slow connection it is the only thing, so it now occupies the space
   deliberately and says something a user would recognise. */
.tk-boot {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--tk-space-4);
    min-height: 55vh;
    padding: var(--tk-space-6) var(--tk-space-4);
}

.tk-boot__spinner {
    width: 34px;
    height: 34px;
    border-radius: var(--tk-radius-full);
    /* Track plus one lit arc, so the direction of travel is readable. */
    border: 3px solid var(--tk-border);
    border-top-color: var(--tk-accent);
    animation: spin 900ms linear infinite;
}

.tk-boot__text {
    font-size: var(--tk-text-sm);
    font-weight: 600;
    color: var(--tk-ink-muted);
    margin: 0;
}

/* The spinner is the only thing moving on the screen, so stopping it dead
   leaves no sign the app is still working. Slowed rather than removed. */
@media (prefers-reduced-motion: reduce) {
    .tk-boot__spinner {
        animation-duration: 2.4s !important;
        animation-iteration-count: infinite !important;
    }
}

/* ====================================================================== */
/* Tailwind utility motion — one curve for the whole product              */
/* ====================================================================== */

/* 63 elements in the markup carry a Tailwind transition utility, and every one
   of them ran on Tailwind's stock cubic-bezier(0.4, 0, 0.2, 1) rather than
   --tk-ease. Converting them one by one would mean 63 markup edits and would
   not stop the next one drifting, so the utilities are re-pointed here instead.

   Only the curve is taken. Durations legitimately differ by purpose -- a 150ms
   hover and a 1s progress fill are both right -- and it is the easing, not the
   duration, that makes motion read as one system. So `duration-*` keeps
   working, and the 13 elements that opt into an explicit `ease-*` are excluded
   rather than overridden, since those are deliberate choices.

   The specificity is deliberately above a bare utility class so it wins; it
   sets a single property, so nothing else is affected. */
body :is(.transition, .transition-all, .transition-colors, .transition-transform,
         .transition-opacity, .transition-shadow):not(.ease-linear):not(.ease-in):not(.ease-out):not(.ease-in-out) {
    transition-timing-function: var(--tk-ease);
}

/* The password-strength bar carried Tailwind's `transition-all duration-300`,
   so it moved on a different curve from every other bar in the product and
   animated `all` rather than the two properties that actually change. Kept
   quicker than the dashboard meters -- it tracks typing, so it should feel
   immediate -- but now on the shared easing. */
.strength-progress {
    transition: width calc(var(--tk-duration) * 2) var(--tk-ease),
                background-color calc(var(--tk-duration) * 2) var(--tk-ease);
}

/* ====================================================================== */
/* Badges & chips                                                          */
/* ====================================================================== */

.tk-badge {
    display: inline-flex;
    align-items: center;
    gap: var(--tk-space-1);
    padding: 0.1875rem 0.5rem;
    border-radius: var(--tk-radius-full);
    font-size: var(--tk-text-xs);
    font-weight: 600;
    background-color: var(--tk-surface-sunken);
    color: var(--tk-ink-secondary);
}

/* ====================================================================== */
/* Segmented control — category filters                                    */
/* ====================================================================== */

/* Replaces a row of pill buttons whose selected state was applied by adding
   and removing eight Tailwind classes in JS (bg-indigo-600, text-white,
   border-indigo-600, shadow-md, font-bold, and their five inactive
   counterparts). That is brittle -- the two lists have to stay perfect
   mirrors -- and it hardcoded an indigo that is no longer in the palette.
   Selection is now one attribute: aria-pressed. */
.tk-segmented {
    display: flex;
    gap: 2px;
    padding: 3px;
    background-color: var(--tk-surface-sunken);
    border-radius: var(--tk-radius);
    /* Scrolls rather than squashing when categories are added or a
       translation runs long -- the old flex-1 pills crushed their own text. */
    overflow-x: auto;
    scrollbar-width: none;
}
.tk-segmented::-webkit-scrollbar { display: none; }

/* A scrolling strip that ends flush at the edge looks like a strip that ends.
   Fading the words out says there is more, and only on the side that actually
   has more -- a permanent fade on both edges would imply content that is not
   there when the row is already at one end.
   Only the category row opts in; the two-option switches never overflow. */
.tk-segmented--scroll {
    --tk-fade-start: 0px;
    --tk-fade-end: 100%;
    -webkit-mask-image: linear-gradient(to right,
        transparent 0, #000 var(--tk-fade-start),
        #000 var(--tk-fade-end), transparent 100%);
    mask-image: linear-gradient(to right,
        transparent 0, #000 var(--tk-fade-start),
        #000 var(--tk-fade-end), transparent 100%);
}
.tk-segmented--scroll.can-scroll-left { --tk-fade-start: 30px; }
.tk-segmented--scroll.can-scroll-right { --tk-fade-end: calc(100% - 30px); }

.tk-segment {
    flex: 1 0 auto;
    min-height: 40px;
    padding: 0.5rem 0.875rem;
    border: 0;
    border-radius: calc(var(--tk-radius) - 2px);
    background: transparent;
    color: var(--tk-ink-secondary);
    font-family: var(--tk-font);
    font-size: var(--tk-text-sm);
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    transition: background-color var(--tk-duration) var(--tk-ease),
                color var(--tk-duration) var(--tk-ease);
    -webkit-tap-highlight-color: transparent;
}

.tk-segment:hover { color: var(--tk-ink); }
.tk-segment:focus-visible { outline: 2px solid var(--tk-accent); outline-offset: -2px; }

/* Weight shifts as well as colour, so selection survives greyscale.
   Scoped through the container: at plain .tk-segment[aria-pressed] specificity
   the background-color declaration lost to another rule while the rest of the
   block applied. */
.tk-segmented .tk-segment[aria-pressed="true"] {
    background-color: var(--tk-surface);
    color: var(--tk-ink);
    font-weight: 700;
    box-shadow: var(--tk-shadow-sm);
}

/* ====================================================================== */
/* Tile grid — catalogue items                                             */
/* ====================================================================== */

.tk-tiles {
    display: grid;
    /* Fits as many as the width allows instead of forcing three columns onto
       a 375px phone, where a fixed h-28 tile left the label clipped.
       Raised from 96px: these are the app's only pictorial controls and were
       being tapped at the size of a chip. 118px still gives three columns on
       a 375px phone, with a bigger glyph and a label that fits on one line. */
    grid-template-columns: repeat(auto-fill, minmax(118px, 1fr));
    gap: var(--tk-space-3);
}

.tk-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--tk-space-2);
    min-height: 132px;
    padding: var(--tk-space-3);
    background-color: var(--tk-surface);
    border: 1px solid var(--tk-border);
    border-radius: var(--tk-radius);
    color: var(--tk-ink);
    font-family: var(--tk-font);
    cursor: pointer;
    text-align: center;
    /* Only colour and a small press displacement animate. The old
       hover:scale-105 does nothing on touch, where there is no hover, and on
       desktop it reflowed neighbouring tiles on every pass. */
    transition: background-color var(--tk-duration) var(--tk-ease),
                border-color var(--tk-duration) var(--tk-ease),
                box-shadow var(--tk-duration) var(--tk-ease),
                transform var(--tk-duration) var(--tk-ease);
    -webkit-tap-highlight-color: transparent;
    /* Raised off the panel it sits on. Flat tiles on a flat container read as
       a wall of text with a picture; the shadow and the lighter face give each
       one an edge to find. */
    background-color: var(--tk-surface-raised);
    box-shadow: var(--tk-shadow-sm);
}

.tk-tile:hover {
    background-color: var(--tk-surface-sunken);
    border-color: var(--tk-border-strong);
    box-shadow: var(--tk-shadow);
    transform: translateY(-1px);
}
.tk-tile:active { transform: translateY(1px); box-shadow: var(--tk-shadow-sm); }
.tk-tile:focus-visible { outline: 2px solid var(--tk-accent); outline-offset: 2px; }

/* A tinted disc behind the glyph, so the icon reads as an object on the tile
   rather than marks floating above the label. */
.tk-tile__icon {
    border-radius: var(--tk-radius-full);
    background-color: var(--tk-surface-sunken);
    padding: 6px;
    box-sizing: border-box;
    transition: background-color var(--tk-duration) var(--tk-ease);
}
.tk-tile:hover .tk-tile__icon { background-color: var(--tk-surface); }

.tk-tile__icon {
    width: 58px;
    height: 58px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
}
.tk-tile__icon svg { width: 100%; height: 100%; }

.tk-tile__label {
    font-size: var(--tk-text-sm);
    font-weight: 600;
    line-height: 1.25;
    /* Two lines then ellipsis, so a long name cannot change the tile height
       and break the grid rhythm. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    word-break: break-word;
}

/* In the log sheet: three across and a third shorter. Fruit was ~860px of
   two-column 132px tiles between the search box and anything you had picked. */
#log-sheet .tk-tiles {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--tk-space-2);
}
#log-sheet .tk-tile {
    position: relative;
    min-height: 92px;
    padding: var(--tk-space-2);
    gap: 4px;
}
#log-sheet .tk-tile__icon { width: 40px; height: 40px; padding: 4px; }
#log-sheet .tk-tile__label { font-size: var(--tk-text-xs); }

/* How many of this are already in the meal. */
.tk-tile[data-count]::after {
    content: attr(data-count);
    position: absolute;
    top: 6px;
    right: 6px;
    min-width: 20px;
    height: 20px;
    padding: 0 6px;
    border-radius: var(--tk-radius-full);
    /* The primary button's pair, not accent-on-white: white on the dark
       theme's #a78bfa is 2.6:1, which is a badge nobody can read. */
    background-color: var(--tk-action);
    color: var(--tk-action-ink);
    font-family: var(--tk-font);
    font-size: 11px;
    font-weight: 800;
    line-height: 20px;
    text-align: center;
}

/* Staged items read as selected rather than merely hovered. */
.tk-tile[aria-pressed="true"] {
    background-color: var(--tk-accent-soft);
    border-color: var(--tk-accent);
    color: var(--tk-accent);
}

/* ====================================================================== */
/* Bottom navigation                                                       */
/* ====================================================================== */

/* Replaces the top-left hamburger. On a phone held right-handed, top-left is
   the furthest point from the thumb and forces a grip shift -- a poor home for
   the control that starts every navigation action in a one-handed PWA used
   while cooking or training. This sits in the thumb arc instead.
   Nothing became unreachable: "More" still opens the same drawer. */
/* Nothing cleared the fixed bar, so the last thing on every page ran underneath
   it -- most visibly the staged item's Add button, which came to rest exactly
   one pixel below the fold. Stating the bar's height once lets the page reserve
   it and lets `scrollIntoView` stop short of it, so revealing a control cannot
   park it behind the nav. */
:root { --tk-nav-h: 57px; }

body { padding-bottom: calc(var(--tk-nav-h) + env(safe-area-inset-bottom, 0px)); }

html {
    scroll-padding-top: var(--tk-space-4);
    scroll-padding-bottom: calc(var(--tk-nav-h) + env(safe-area-inset-bottom, 0px) + var(--tk-space-4));
}

.tk-bottomnav {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 45; /* under modals (50+), over page content */
    display: flex;
    align-items: stretch;
    justify-content: space-around;
    background-color: var(--tk-surface);
    border-top: 1px solid var(--tk-border);
    /* Standalone PWA: without this the bar sits under the iPhone home indicator. */
    padding-bottom: env(safe-area-inset-bottom, 0px);
    box-shadow: 0 -1px 3px rgba(10, 14, 20, 0.06);
}

html.dark .tk-bottomnav { box-shadow: 0 -1px 3px rgba(0, 0, 0, 0.5); }

/* On a wide screen the bar stretched the whole window while the page sat in a
   672px column down the middle, so its five items were 305px apart with the
   outer two pinned to the edges of the monitor. It belongs over the content,
   not over the desk. Measured at 1280: the column is 624 wide at x=328. */
@media (min-width: 768px) {
    .tk-bottomnav {
        max-width: 42rem;
        margin: 0 auto;
        border-left: 1px solid var(--tk-border);
        border-right: 1px solid var(--tk-border);
        border-top-left-radius: var(--tk-radius);
        border-top-right-radius: var(--tk-radius);
    }
}

.tk-navitem {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    padding: 0.5rem 0.25rem;
    min-height: 56px;
    border: 0;
    background: none;
    color: var(--tk-ink-muted);
    font-family: var(--tk-font);
    font-size: 0.6875rem;
    font-weight: 600;
    cursor: pointer;
    transition: color var(--tk-duration) var(--tk-ease);
    -webkit-tap-highlight-color: transparent;
}

.tk-navitem svg { width: 22px; height: 22px; }
.tk-navitem:hover { color: var(--tk-ink-secondary); }
.tk-navitem:focus-visible { outline: 2px solid var(--tk-accent); outline-offset: -2px; }

/* Selection is carried by weight and a bar as well as colour, so it survives
   greyscale and colour-blindness. */
.tk-navitem[aria-current="page"] {
    color: var(--tk-ink);
    font-weight: 700;
}
.tk-navitem[aria-current="page"]::before {
    content: "";
    position: absolute;
    top: 0;
    width: 28px;
    height: 2px;
    border-radius: 0 0 2px 2px;
    background-color: var(--tk-ink);
}
.tk-navitem { position: relative; }

/* The primary action. Logging food is the overwhelming majority of what
   anyone does here, so it is the largest target and the only filled one. */
.tk-navitem--action {
    flex: 0 0 auto;
    justify-content: flex-start;
    padding-top: 0;
}

.tk-navaction {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 52px;
    height: 52px;
    margin-top: -18px;
    border-radius: var(--tk-radius-full);
    background-color: var(--tk-action);
    color: var(--tk-action-ink);
    border: 3px solid var(--tk-surface);
    box-shadow: var(--tk-shadow);
    transition: transform var(--tk-duration) var(--tk-ease),
                background-color var(--tk-duration) var(--tk-ease);
}
.tk-navaction svg { width: 26px; height: 26px; }
.tk-navitem--action:hover .tk-navaction { background-color: var(--tk-action-hover); }
.tk-navitem--action:active .tk-navaction { transform: scale(0.94); }

/* Hidden while a modal is open, matching the old hamburger's behaviour, and
   whenever the app shell itself is hidden (loading, auth, verification).
   The More sheet is deliberately not in this list: it rises over the bar and
   the scrim dims it, so hiding the bar only opened a gap during the slide. */
.body-modal-open .tk-bottomnav,
.modal-open-locked .tk-bottomnav { display: none; }

#app-content.hidden ~ .tk-bottomnav { display: none; }

@media (prefers-reduced-motion: reduce) {
    .tk-navaction { transition: none; }
}

/* ====================================================================== */
/* AI Coach                                                                */
/* ====================================================================== */

/* What the coach cannot see, said rather than enforced. The previous design
   blocked the whole feature behind a red warning triangle when a weight entry
   was missing or a week old -- so someone who logs food daily and never uses a
   scale was permanently locked out of something they pay for. */
.tk-coachnote {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--tk-space-2);
    padding: var(--tk-space-2) var(--tk-space-3);
    border: 1px solid var(--tk-border);
    border-left: 3px solid var(--tk-accent);
    border-radius: var(--tk-radius);
    background-color: var(--tk-surface-sunken);
    font-family: var(--tk-font);
    font-size: var(--tk-text-xs);
    line-height: 1.4;
    color: var(--tk-ink-secondary);
}

.tk-coachnote__action {
    flex: 0 0 auto;
    padding: 3px var(--tk-space-3);
    border: 0;
    border-radius: var(--tk-radius-full);
    background-color: var(--tk-accent);
    color: #ffffff;
    font-family: var(--tk-font);
    font-size: var(--tk-text-xs);
    font-weight: 700;
    cursor: pointer;
}

/* The empty state is an invitation, not a warning. It was a red alert
   triangle, which is the wrong register for "you are new here". */
.tk-coachempty__icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 84px;
    height: 84px;
    border-radius: var(--tk-radius-full);
    background-color: var(--tk-accent-soft);
    color: var(--tk-accent);
}

/* ---- Suggested questions --------------------------------------------- */

/* An empty box labelled "What are you struggling with?" assumes the user
   already knows what this can answer. Most do not, and the ones who guess ask
   it the time. */
.tk-prompts {
    display: flex;
    flex-wrap: wrap;
    gap: var(--tk-space-2);
    margin-top: var(--tk-space-2);
}

.tk-prompt {
    padding: 5px var(--tk-space-3);
    border: 1px solid var(--tk-border-strong);
    border-radius: var(--tk-radius-full);
    background-color: var(--tk-surface);
    color: var(--tk-ink-secondary);
    font-family: var(--tk-font);
    font-size: var(--tk-text-xs);
    font-weight: 600;
    text-align: left;
    cursor: pointer;
    transition: background-color var(--tk-duration) var(--tk-ease),
                color var(--tk-duration) var(--tk-ease);
    -webkit-tap-highlight-color: transparent;
}
.tk-prompt:hover {
    background-color: var(--tk-accent-soft);
    color: var(--tk-accent);
}

/* ====================================================================== */
/* Goal snapshot and path                                                  */
/* ====================================================================== */

/* The wizard asked for height, weight, age and activity and gave nothing back
   except one figure buried in a paragraph. These are the numbers the plan is
   built from, so they are shown as numbers -- and the same shape as the macro
   tiles, because they are the same kind of thing. */

.tk-snapshot {
    padding: var(--tk-space-3);
    border: 1px solid var(--tk-border);
    border-radius: var(--tk-radius-lg);
    background-color: var(--tk-surface-sunken);
}

.tk-snapshot__head {
    font-family: var(--tk-font);
    font-size: var(--tk-text-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--tk-ink-muted);
    margin: 0 0 var(--tk-space-2);
    text-align: center;
}

.tk-snapshot__grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--tk-space-2);
}

.tk-snapshot__stat {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding: var(--tk-space-2) 4px;
    background-color: var(--tk-surface);
    border: 1px solid var(--tk-border);
    border-radius: var(--tk-radius);
    text-align: center;
}

.tk-snapshot__value {
    font-family: var(--tk-font);
    font-size: var(--tk-text-lg);
    font-weight: 700;
    color: var(--tk-ink);
    line-height: 1.1;
}

.tk-snapshot__label {
    font-family: var(--tk-font);
    font-size: 10px;
    font-weight: 600;
    color: var(--tk-ink-muted);
}

/* ---- The path -------------------------------------------------------- */

/* A date on its own is not a plan: it hides the rate, and the rate is what
   decides whether the plan is sensible. */
.tk-path {
    padding: var(--tk-space-3);
    border: 1px solid var(--tk-border);
    border-left: 3px solid var(--tk-good);
    border-radius: var(--tk-radius);
    background-color: var(--tk-surface);
}

/* A pace that would cost lean mass is not a success message. */
.tk-path.is-warning { border-left-color: var(--tk-warning); }

.tk-path__rate {
    margin: 0;
    font-family: var(--tk-font);
    font-size: var(--tk-text-sm);
    font-weight: 700;
    color: var(--tk-ink);
}

.tk-path__when {
    margin: 2px 0 0;
    font-family: var(--tk-font);
    font-size: var(--tk-text-xs);
    color: var(--tk-ink-secondary);
}

.tk-path__warn {
    margin: var(--tk-space-2) 0 0;
    font-family: var(--tk-font);
    font-size: var(--tk-text-xs);
    line-height: 1.4;
    color: var(--tk-ink-secondary);
}

/* ====================================================================== */
/* Achievement badges                                                      */
/* ====================================================================== */

/* Achievements were awarded, stored and mirrored to publicProfiles from the
   start, and displayed nowhere -- the modal meant to hold them was never
   filled or opened. They are the one thing a profile can show about someone
   else that is both true and already public, so they belong on the page
   itself rather than behind a tap. */

.tk-badges {
    display: flex;
    flex-wrap: wrap;
    gap: var(--tk-space-2);
}

.tk-badge {
    display: inline-flex;
    align-items: center;
    gap: var(--tk-space-2);
    padding: 6px var(--tk-space-3) 6px var(--tk-space-2);
    border: 1px solid var(--tk-border);
    border-radius: var(--tk-radius-full);
    background-color: var(--tk-surface);
    font-family: var(--tk-font);
    font-size: var(--tk-text-xs);
    font-weight: 600;
    color: var(--tk-ink);
    /* Earned badges only, so there is no locked state to grey out. */
}

.tk-badge__icon {
    font-size: 1rem;
    line-height: 1;
}

/* ====================================================================== */
/* The logging card                                                        */
/* ====================================================================== */

/* One card used to hold four unrelated things in a flat stack: the input, a
   status line, eleven category chips with their expandable grid, and the
   result panel with its macros and multiplier. Nothing separated them, so
   confirming a result meant reading past the whole browse UI to reach the
   Add button. The zones are explicit now, and only one of browse/result is
   ever on screen. */

/* Real space between zones. Margin alone was not enough to read as a
   boundary: the card looked like one long stack whose parts happened to be
   spaced differently. */
.tk-logzone + .tk-logzone { margin-top: var(--tk-space-4); }

/* The divider belongs to the browse zone specifically. The result panel is a
   filled box with its own border, so a hairline above it would just be a line
   sitting against the edge of that box -- which is why this is not written as
   `.tk-logzone + .tk-logzone`. */
#logging-browse {
    padding-top: var(--tk-space-4);
    border-top: 1px solid var(--tk-border);
}

/* ---- The search bar ---------------------------------------------------- */

/* Search was a full-width button under the field, with mic and barcode beside
   it -- a whole row spent on controls, above a second row of category chips.
   The bar holds them now: the capture affordances sit inside it while it is
   empty, and are replaced by Search the moment there is something to search
   for. The two states are mutually exclusive, so nothing is lost. */

.tk-searchbar {
    display: flex;
    align-items: flex-end;
    gap: var(--tk-space-2);
    /* The left inset carries the text away from the border. At 8px it read as
       touching it, because the animated border wrapper eats 3px of its own
       before this padding even starts. */
    padding: 4px 4px 4px var(--tk-space-4);
}

.tk-searchbar textarea {
    flex: 1 1 auto;
    min-width: 0;
    border: 0;
    background: transparent;
    resize: none;
    padding: 10px 0;
    font-size: var(--tk-text-base);
}
.tk-searchbar textarea:focus { outline: none; box-shadow: none; }

/* Both groups stay in the flow and trade places by animating, rather than
   being switched with display -- which cannot transition, so the swap landed
   in a single frame and read as a glitch rather than a change of state.
   Width, opacity and scale together give the field the feeling of widening to
   meet what you are typing.
   The curve is front-loaded and settles without overshoot; an easeInOut of the
   same length feels slower because the middle is where all the distance is. */
.tk-searchbar__actions,
.tk-searchbar__submit {
    /* max-width rather than width: the group has no fixed size, and animating
       to a hard-coded one would clip the icons the moment anything changes. */
    transition: max-width 260ms cubic-bezier(0.22, 1, 0.36, 1),
                opacity 160ms cubic-bezier(0.22, 1, 0.36, 1),
                transform 260ms cubic-bezier(0.22, 1, 0.36, 1),
                margin-left 260ms cubic-bezier(0.22, 1, 0.36, 1);
    overflow: hidden;
}

.tk-searchbar__actions {
    display: flex;
    align-items: center;
    gap: 4px;
    flex: 0 0 auto;
    padding-bottom: 2px;
    /* Two 40px buttons and the gap between them. */
    max-width: 92px;
    opacity: 1;
    transform: scale(1);
}

/* The avatar menu's three rows. Tailwind's `py-2` leaves them 42px tall and
   their ink is `text-gray-700 dark:text-gray-200` -- pre-token, like the log
   rows were. The panel itself is still Tailwind; this brings the rows up to the
   touch-target floor and onto the palette without unpicking it.

   `.tk-iconbtn` is deliberately left at 40px: its own comment reasons that
   three of them plus the gaps clear the floor on a 360px phone, and that is a
   considered trade rather than an oversight. */
#profile-dropdown a {
    display: flex;
    align-items: center;
    min-height: 44px;
    color: var(--tk-ink);
}
#profile-dropdown a:hover { background-color: var(--tk-surface-sunken); }

/* The account avatar. It was `bg-indigo-500` with white text -- an off-palette
   Tailwind indigo left from before the tokens, and 4.47:1, just under AA. The
   accent already means "this is yours" everywhere else, and --tk-ink-inverse
   flips with the theme so the initial stays legible on it: 5.70 on the light
   violet, 7.11 on the dark one. */
#profile-avatar-btn {
    background-color: var(--tk-accent);
    color: var(--tk-ink-inverse);
}
#profile-avatar-btn:hover { background-color: var(--tk-accent-hover); }

/* Controls that a finger has to find, brought up to the 44px floor the rest of
   the app holds. Measured on the live page, not guessed:

     modal close ×          16 x 32
     Generate / Manual      168 x 28   the goals wizard's segmented tabs
     wizard inputs          166 x 37

   The close button is the worst of them and appears on every modal. It stays
   visually the same size -- the glyph is 24px of text -- the box around it
   simply becomes catchable.

   `.tk-iconbtn` is NOT in this list on purpose: its own comment reasons that
   40px plus the gaps clears the floor for three of them on a 360px phone, and
   growing it would push the camera off the row. */
.modal-box button[id^="close-"],
.modal-box .modal-header button[id*="close"] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
}

/* The goals wizard's segmented control.
   `switchGoalsTab` used to assign `className` wholesale --
   `bg-white text-indigo-600 dark:bg-slate-600 dark:text-white` for the active
   half and `text-gray-500 dark:text-gray-400` for the other -- which is why the
   `tk-accent-text` and `tk-ink-faint` classes in the markup never survived the
   first render, and why the inactive label measured 4.07:1 on the light track.
   Four off-palette Tailwind colours, replaced by one class the JS can toggle. */
.tk-seg {
    flex: 1;
    min-height: 44px;
    border: 0;
    border-radius: var(--tk-radius-sm);
    background: none;
    color: var(--tk-ink-muted);
    font-family: var(--tk-font);
    font-size: var(--tk-text-xs);
    font-weight: 600;
    cursor: pointer;
    transition: background-color var(--tk-duration) var(--tk-ease),
                color var(--tk-duration) var(--tk-ease);
}
.tk-seg:hover { color: var(--tk-ink); }
.tk-seg:focus-visible { outline: 2px solid var(--tk-accent); outline-offset: 2px; }
.tk-seg.is-on {
    background-color: var(--tk-surface-raised);
    color: var(--tk-ink);
    box-shadow: var(--tk-shadow-sm);
}

.wizard-input { min-height: 44px; }

.tk-iconbtn {
    display: flex;
    align-items: center;
    justify-content: center;
    /* 40px, so three of them fit beside the field on a 360px phone and each
       still clears the touch-target floor with the gap included. */
    width: 40px;
    height: 40px;
    border: 0;
    border-radius: var(--tk-radius-full);
    background-color: var(--tk-surface-sunken);
    color: var(--tk-ink-secondary);
    cursor: pointer;
    transition: background-color var(--tk-duration) var(--tk-ease),
                color var(--tk-duration) var(--tk-ease),
                transform 120ms var(--tk-ease);
    -webkit-tap-highlight-color: transparent;
}
.tk-iconbtn:hover { color: var(--tk-ink); }
.tk-iconbtn:active { transform: scale(0.94); }
.tk-iconbtn:focus-visible { outline: 2px solid var(--tk-accent); outline-offset: 2px; }
.tk-iconbtn svg { width: 19px; height: 19px; }

/* The mic is the one that changes state, so it is the one that carries colour. */
.tk-iconbtn--mic { background-color: var(--tk-accent-soft); color: var(--tk-accent); }
.tk-iconbtn--danger { background-color: var(--tk-critical); color: #ffffff; }

/* Pro features carry a corner dot rather than a padlock: the lock reads as
   "you cannot", the dot as "there is more here". */
/* The dot that marks a Pro control. It was --tk-streak, which everywhere else
   in the app means a run of logged days, so an amber pip on the camera read as
   a notification about something rather than as "this one needs Pro". Violet is
   the accent this product reserves for Pro; the button also says so now in its
   own label, because a dot alone cannot. */
.tk-iconbtn--pro { position: relative; }
.tk-iconbtn--pro::after {
    content: "";
    position: absolute;
    top: 3px;
    right: 3px;
    width: 7px;
    height: 7px;
    border-radius: var(--tk-radius-full);
    background-color: var(--tk-accent);
}

/* An arrow, not the word "Search". At this size the label was most of the
   control, and the arrow says the same thing in a circle the same size as the
   buttons it replaces -- so the row does not change height as it swaps. */
.tk-searchbar__submit {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    justify-content: center;
    /* 36px, inset from the field's edge. At 40px the filled circle ran to
       within a pixel of the border and read as pressed against it. */
    width: 36px;
    height: 36px;
    padding: 0;
    border: 0;
    border-radius: var(--tk-radius-full);
    background-color: var(--tk-action);
    color: var(--tk-action-ink);
    cursor: pointer;
    margin: 0 3px 4px 0;
    -webkit-tap-highlight-color: transparent;
    /* Resting state is away: collapsed, faded, slightly small. */
    max-width: 0;
    opacity: 0;
    transform: scale(0.7);
    pointer-events: none;
}
.tk-searchbar__submit svg { width: 17px; height: 17px; }
.tk-searchbar__submit:active { transform: scale(0.95); }

.tk-searchbar.is-typing .tk-searchbar__actions {
    max-width: 0;
    opacity: 0;
    transform: scale(0.7);
    margin-left: -4px;
    pointer-events: none;
}
.tk-searchbar.is-typing .tk-searchbar__submit {
    max-width: 40px;
    opacity: 1;
    transform: scale(1);
    pointer-events: auto;
}

/* While dictating, the capture strip stays put: a transcript arriving would
   otherwise swap Stop for Search under the user's thumb mid-sentence. */
.tk-searchbar.is-recording .tk-searchbar__submit {
    max-width: 0;
    opacity: 0;
    pointer-events: none;
}
.tk-searchbar.is-recording .tk-searchbar__actions {
    max-width: 92px;
    opacity: 1;
    transform: scale(1);
    margin-left: 0;
    pointer-events: auto;
}

@media (prefers-reduced-motion: reduce) {
    .tk-searchbar__actions,
    .tk-searchbar__submit { transition: opacity 100ms linear; }
}

/* ---- Capture menu ------------------------------------------------------ */

/* Photo and barcode are the same gesture -- point the camera at the food --
   so they share one button in the bar and separate here. That is one control
   in the bar instead of two, at the cost of one tap on a flow that already
   opens a camera. */
/* Anchored to the wrapper around the bar, not to the button inside it: the
   bar clips on two counts -- the animated border needs overflow:hidden, and so
   does the width transition on the capture strip -- so a popover positioned
   within it renders and is then invisible. */
.tk-searchwrap { position: relative; }

/* Entering result mode hides the browse zone rather than pushing it up the
   page, so the result is the only thing the card is asking about. The status
   line is cleared instead of hidden -- a barcode lookup reports progress there
   while its panel is still showing a spinner. */
#logging-section.is-resulting #logging-browse { display: none; }

/* ...except while picking from the catalogue, which is a multi-item selection
   by design -- the panel it fills in is literally headed as one. Hiding the
   browser after every single tile meant adding a second fruit cost reopening
   Browse and reselecting the category. */
#logging-section.is-resulting.is-picking #logging-browse { display: block; }

/* ---- Picking: the result is a tray at the foot of the sheet ----------

   While picking from the catalogue the tiles stay open and the staged meal
   pins to the bottom of the sheet: its head (how many, what they come to),
   a short list of its own, and Add. It used to sit above the tiles and the
   sheet scrolled to it on every tap, so each next tile was a scroll away. */
#logging-section.is-picking {
    display: flex;
    flex-direction: column;
}
#logging-section.is-picking > * { flex: none; }
#logging-section.is-picking #ai-result-container {
    order: 10;
    position: sticky;
    /* Over the sheet body's bottom padding, not above it: at 0 it stopped
       16px short of the edge, with a strip of tiles showing under Add. */
    bottom: calc(-1 * var(--tk-space-4));
    z-index: 4;
    margin-top: var(--tk-space-3);
    padding: var(--tk-space-3) var(--tk-space-3) 0;
    border: 1px solid var(--tk-border-strong);
    border-radius: var(--tk-radius-lg) var(--tk-radius-lg) 0 0;
    background-color: var(--tk-surface);
    box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.28);
}
#logging-section.is-picking .tk-result__head { margin-bottom: var(--tk-space-2); }
#logging-section.is-picking #individual-view {
    /* A few rows, then the list scrolls inside the tray and the tiles keep
       the rest of the sheet. */
    /* Two rows, then the list scrolls inside the tray. At 220px the rows
       were most of the sheet and the tiles were under them. */
    max-height: 132px;
    overflow-y: auto;
    overscroll-behavior: contain;
    display: grid;
    gap: 6px;
}
#logging-section.is-picking #individual-view > * { margin: 0; }
/* The tray's rows sit on the tray, not on a second panel. */
#logging-section.is-picking .tk-item { background-color: var(--tk-surface-sunken); }
/* All four in one strip: the tray is a summary, but "how much of my
   macros will this use" was the question, and calories alone did not
   answer it. Label, bar, what is left -- a quarter each, the phone's
   compact ImpactPreview. */
#logging-section.is-picking #result-impact { margin-top: var(--tk-space-2); padding: 0; background: none; }
#logging-section.is-picking #result-impact .tk-impact__title { display: none; }
#logging-section.is-picking #result-impact .tk-impact__rows { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--tk-space-3); }
#logging-section.is-picking #result-impact .tk-impact__row { grid-template-columns: 1fr; gap: 2px; line-height: 1.2; }
#logging-section.is-picking #result-impact .tk-impact__label { font-size: 10px; color: var(--tk-ink-muted); font-weight: 700; }
#logging-section.is-picking #result-impact .tk-impact__figure { min-width: 0; text-align: left; font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#logging-section.is-picking .tk-result__actions {
    /* The tray is already pinned. Left sticky, the bar rode 16px up inside it
       -- over the calories line, with an empty band beneath the buttons. */
    position: static;
    margin-top: var(--tk-space-2);
    padding-bottom: var(--tk-space-3);
    margin-left: calc(-1 * var(--tk-space-3));
    margin-right: calc(-1 * var(--tk-space-3));
    padding-left: var(--tk-space-3);
    padding-right: var(--tk-space-3);
}
/* Room under the last tiles for the tray to sit over. */
#logging-section.is-picking #logging-browse { padding-bottom: var(--tk-space-2); }

/* A section label, the phone's way: small caps above the card rather than a
   heading centred inside it. `sectionTitle` in the phone's screens is the
   same thing -- 0.75rem, 700, uppercase, 0.6px of tracking, muted ink -- and
   the two are compared in mobile/src/__tests__/paletteParity.test.ts. */
.tk-sectionhead {
    margin: 0 0 var(--tk-space-2);
    font-family: var(--tk-font);
    font-size: var(--tk-text-xs);
    font-weight: 700;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    color: var(--tk-ink-muted);
}

/* A struck row: still on screen, not in the meal, one tap from coming back.
   The phone's scan review has worked this way since it shipped -- a receipt
   is half somebody else's, and a line struck by mistake should not cost a
   re-scan. */
.tk-item--gone .tk-item__name,
.tk-item--gone .tk-item__cals,
.tk-item--gone .tk-item__bottom {
    opacity: 0.45;
    text-decoration: line-through;
}
.tk-item--gone .tk-item__bottom { pointer-events: none; }
.tk-item__undo {
    font-family: var(--tk-font);
    font-size: var(--tk-text-xs);
    font-weight: 800;
    color: var(--tk-accent);
    text-decoration: none;
}

/* ---- Reviewing a result ------------------------------------------------

   A photo, a scan or a search result is being checked, so the ways of logging
   something else are done with: Photo, Barcode and Manual go, the manual form
   with them, and the search box too unless the result came from typing in it
   (.is-typed), where the query is context and refining it is the next move.
   Not while picking from the catalogue: there the tiles are the point. */
#logging-section.is-resulting:not(.is-picking) .tk-routes,
#logging-section.is-resulting:not(.is-picking) #manual-content { display: none; }
#logging-section.is-resulting:not(.is-picking):not(.is-typed) .tk-searchwrap { display: none; }

/* The meal's protein, carbs and fat, beside its name. */
.tk-result__totals {
    display: flex;
    gap: var(--tk-space-2);
    margin: 0;
    font-family: var(--tk-font);
    font-size: var(--tk-text-xs);
    font-variant-numeric: tabular-nums;
}
.tk-result__totals:empty { display: none; }
.tk-result__totals b { font-weight: 800; }
.tk-result__p { color: var(--tk-protein); }
.tk-result__c { color: var(--tk-carbs); }
.tk-result__f { color: var(--tk-fat); }

/* The status line reserved a fixed 1.25rem whether or not it had anything to
   say, which read as a gap between the buttons and the categories. It takes
   space only when it is actually saying something. */
.tk-loghint {
    margin: 0;
    font-family: var(--tk-font);
    font-size: var(--tk-text-sm);
    text-align: center;
    color: var(--tk-ink-muted);
    /* :empty cannot see whitespace-only text, and the reset path writes "". */
    min-height: 0;
}
.tk-loghint:empty { display: none; }

/* ---- Result panel ------------------------------------------------------ */

/* A filled panel with no border and no scroll of its own. It was a bordered
   box with a 60vh scroll area inside the page's scroll -- two things to
   scroll on one screen, and the Individual view's list cut off mid-card. */
.tk-result {
    background-color: var(--tk-surface-sunken);
    border-radius: var(--tk-radius-lg);
    padding: var(--tk-space-4);
}

.tk-result__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--tk-space-3);
    flex-wrap: wrap;
    margin-bottom: var(--tk-space-3);
}

.tk-result__title {
    font-family: var(--tk-font);
    font-size: var(--tk-text-base);
    font-weight: 700;
    color: var(--tk-ink);
    margin: 0;
    /* A pasted meal can be a paragraph; the panel must not grow a title bar. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 100%;
}

.tk-result__meta {
    display: flex;
    justify-content: center;
    gap: var(--tk-space-3);
    font-family: var(--tk-font);
    font-size: var(--tk-text-xs);
    color: var(--tk-ink-muted);
}

/* The view switch was a two-sided toggle -- a label, a switch, another label --
   which never said which side was active. A segmented control shows the
   current view instead of asking you to infer it. */
.tk-viewswitch { flex: 0 0 auto; }

/* ---- Macros ------------------------------------------------------------ */

.tk-macros {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: var(--tk-space-2);
}

/* Four figures on the panel, not four outlined boxes on it. */
.tk-macro {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding: var(--tk-space-2) 4px;
    background-color: var(--tk-surface);
    border-radius: var(--tk-radius);
    text-align: center;
}

.tk-macro__label {
    font-family: var(--tk-font);
    font-size: 11px;
    white-space: nowrap;
    font-weight: 600;
    color: var(--tk-ink-muted);
}

/* The macro figure is an editable input for three of the four, and static text
   for calories. They have to look identical or the card reads as broken. */
.tk-macro__value,
input.tk-macro__value {
    width: 100%;
    padding: 0;
    border: 0;
    background: transparent;
    font-family: var(--tk-font);
    font-size: var(--tk-text-lg);
    font-weight: 700;
    line-height: 1.2;
    text-align: center;
    color: var(--tk-ink);
}
input.tk-macro__value:focus {
    outline: none;
    box-shadow: inset 0 -2px 0 var(--tk-accent);
}

/* Hue carries which macro this is, matching the dashboard series. Calories are
   a derived total, so they stay ink rather than taking a fourth hue. */
.tk-macro--protein .tk-macro__value { color: var(--tk-protein); }
.tk-macro--carbs .tk-macro__value { color: var(--tk-carbs); }
.tk-macro--fat .tk-macro__value { color: var(--tk-fat); }

/* ---- Photo logging ----------------------------------------------------- */

.tk-photodrop {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--tk-space-2);
    width: 100%;
    min-height: 150px;
    padding: var(--tk-space-5);
    /* Dashed, because it is an empty slot waiting to be filled rather than a
       button that does something on its own. */
    border: 2px dashed var(--tk-border-strong);
    border-radius: var(--tk-radius-lg);
    background-color: var(--tk-surface-sunken);
    color: var(--tk-ink-secondary);
    font-family: var(--tk-font);
    font-size: var(--tk-text-sm);
    font-weight: 600;
    cursor: pointer;
    transition: border-color var(--tk-duration) var(--tk-ease),
                color var(--tk-duration) var(--tk-ease);
}
.tk-photodrop:hover { border-color: var(--tk-accent); color: var(--tk-ink); }
.tk-photodrop svg { width: 34px; height: 34px; }

.tk-photopreview {
    position: relative;
    margin: 0;
    border-radius: var(--tk-radius-lg);
    overflow: hidden;
    background-color: var(--tk-surface-sunken);
}

.tk-photopreview img {
    display: block;
    width: 100%;
    /* Fixed frame: a portrait phone photo would otherwise push the note field
       and the Estimate button off the bottom of the sheet. */
    max-height: 260px;
    object-fit: cover;
}

.tk-photopreview__clear {
    position: absolute;
    top: 8px;
    right: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border: 0;
    border-radius: var(--tk-radius-full);
    background-color: rgba(10, 14, 20, 0.6);
    color: #ffffff;
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
}

/* ---- Individual item cards --------------------------------------------- */

/* The per-item cards sat in the same panel as the consolidated macros while
   colouring protein red, carbs green and fat yellow -- so one card showed two
   different schemes for the same three numbers, and "carbs green / fat yellow"
   directly contradicted the dashboard. They read from the same tokens now. */

/* A staged item is two lines: name, calories and remove; then the amount and
   P/C/F inline. It was a 310px card with three labelled columns of figures,
   so two items filled the sheet. */
.tk-item {
    position: relative;
    padding: var(--tk-space-2) var(--tk-space-3);
    border-radius: var(--tk-radius);
    background-color: var(--tk-surface);
    transition: background-color 600ms var(--tk-ease), box-shadow 600ms var(--tk-ease);
}
/* The row a tile tap just added, for a moment -- the tray says so where the
   eye already is rather than in a toast. */
.tk-item--new {
    background-color: var(--tk-accent-soft);
    box-shadow: inset 0 0 0 1px var(--tk-accent);
}
.tk-item__top {
    display: flex;
    align-items: center;
    gap: var(--tk-space-2);
}
.tk-item__top .tk-item__name { flex: 1 1 auto; min-width: 0; padding-right: 0; }
.tk-item__bottom {
    display: flex;
    align-items: center;
    justify-content: space-between;
    /* One line on a phone. It wrapped -- amount on one line, the three macro
       boxes on the next -- which made each row 104px. It may still wrap on
       something narrower than a phone rather than overflow. */
    flex-wrap: wrap;
    gap: 6px var(--tk-space-2);
    margin-top: 4px;
}
.tk-item__bottom .tk-item__qty { gap: 6px; }

.tk-item__name {
    font-family: var(--tk-font);
    font-size: var(--tk-text-sm);
    font-weight: 700;
    color: var(--tk-ink);
    line-height: 1.25;
    padding-right: var(--tk-space-5);
    margin: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tk-item__remove {
    flex: none;
    margin-right: -6px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border: 0;
    border-radius: var(--tk-radius-full);
    background: transparent;
    color: var(--tk-ink-muted);
    cursor: pointer;
    transition: color var(--tk-duration) var(--tk-ease);
}
.tk-item__remove:hover { color: var(--tk-critical-ink); }
.tk-item__remove svg { width: 15px; height: 15px; }

.tk-item__qty {
    display: flex;
    align-items: center;
    gap: var(--tk-space-2);
    min-width: 0;
}

.tk-item__qty input {
    width: 3.25rem;
    padding: 2px 4px;
    font-size: var(--tk-text-xs);
    text-align: center;
}

/* Was a fixed 24px with `truncate`, so every unit longer than two characters
   rendered as "e..." or "se...". It takes the width its text needs. */
.tk-item__unit {
    font-family: var(--tk-font);
    font-size: var(--tk-text-xs);
    color: var(--tk-ink-muted);
    white-space: nowrap;
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The weight/count switch. It names the unit you would move *to*, so the
   control reads as the alternative rather than as the current state -- there is
   already a unit label beside the number saying what you are in. Only foods
   that know what one of them weighs get one. */
.tk-item__mode {
    flex: none;
    font-family: var(--tk-font);
    font-size: var(--tk-text-xs);
    font-weight: 600;
    line-height: 1;
    color: var(--tk-ink-secondary);
    background-color: var(--tk-surface-sunken);
    border: 1px solid var(--tk-border);
    border-radius: var(--tk-radius-sm);
    padding: 4px 8px;
    cursor: pointer;
    white-space: nowrap;
    transition: background-color var(--tk-duration) var(--tk-ease),
                border-color var(--tk-duration) var(--tk-ease),
                color var(--tk-duration) var(--tk-ease);
}
.tk-item__mode:hover { color: var(--tk-ink); border-color: var(--tk-border-strong); }
.tk-item__mode:focus-visible { outline: 2px solid var(--tk-accent); outline-offset: 2px; }

.tk-item__cals {
    font-family: var(--tk-font);
    font-size: var(--tk-text-sm);
    font-weight: 700;
    color: var(--tk-ink);
    white-space: nowrap;
    flex: 0 0 auto;
}
.tk-item__cals span {
    font-size: 10px;
    font-weight: 600;
    color: var(--tk-ink-muted);
}

.tk-item__macros {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 0 0 0 auto;
}

.tk-item__macro {
    display: inline-flex;
    align-items: baseline;
    gap: 2px;
    text-align: left;
    cursor: text;
}
.tk-item__macro > span {
    font-family: var(--tk-font);
    font-size: 10px;
    font-weight: 700;
    color: var(--tk-ink-muted);
}

/* Calories run to four figures where a macro runs to three. */
.tk-item__macro--kcal input { width: 2.8rem; }
/* Not in the catalogue tray. A picked tile carries reference figures, not an
   estimate to correct, and the fourth figure wrapped every tray row onto a
   second line -- 98px against the 72 the tray was redesigned to. The phone
   draws its tray the same way: no calorie field there, four in the scan
   review. The headline kcal still shows. */
#logging-section.is-picking .tk-item__macro--kcal { display: none; }

/* Figures rather than boxes: editable still, but they read as a line of
   text beside the amount, which is what fits. */
.tk-item__macro input {
    width: 2.1rem;
    padding: 1px 0;
    border: 0;
    border-bottom: 1px solid var(--tk-border);
    border-radius: 0;
    background: transparent;
    font-family: var(--tk-font);
    font-size: var(--tk-text-xs);
    font-weight: 700;
    text-align: center;
}
.tk-item__macro input:focus {
    outline: none;
    border-bottom-color: var(--tk-accent);
}

.tk-item__macro label {
    display: block;
    font-family: var(--tk-font);
    font-size: 9px;
    font-weight: 600;
    color: var(--tk-ink-muted);
    text-align: center;
}

/* Same three hues as the consolidated tiles and the dashboard series. */
.tk-item__macro--protein input { color: var(--tk-protein); }
.tk-item__macro--carbs input { color: var(--tk-carbs); }
.tk-item__macro--fat input { color: var(--tk-fat); }

/* ---- Multiplier -------------------------------------------------------- */

.tk-multiplier {
    display: flex;
    align-items: center;
    gap: var(--tk-space-3);
    margin-top: var(--tk-space-3);
    padding: var(--tk-space-2) var(--tk-space-3);
    background-color: var(--tk-surface);
    border-radius: var(--tk-radius);
}
/* The slider in the product's accent, not the browser's default blue. */
.tk-multiplier input[type="range"], #scan-zoom-slider { accent-color: var(--tk-accent); }

.tk-multiplier__label {
    font-family: var(--tk-font);
    font-size: var(--tk-text-xs);
    font-weight: 600;
    color: var(--tk-ink-muted);
    flex: 0 0 auto;
}

.tk-multiplier input[type="range"] { flex: 1 1 auto; min-width: 0; }

/* The grams field in the barcode view shares the multiplier row's shape, so
   "how much of this" looks the same whether it is typed or dragged. */
.tk-amountinput {
    flex: 1 1 auto;
    min-width: 0;
    padding: 4px var(--tk-space-2);
    font-family: var(--tk-font);
    font-size: var(--tk-text-sm);
    font-weight: 700;
    text-align: right;
}

/* The per-100g / per-serving switch, full width because it is the only thing
   on its row and the two options deserve equal weight. */
.tk-basisswitch { margin-bottom: var(--tk-space-3); }

.tk-multiplier__value {
    font-family: var(--tk-font);
    font-size: var(--tk-text-sm);
    font-weight: 700;
    color: var(--tk-ink);
    /* A minimum width so the slider does not twitch as the number changes,
       but not a fixed one: the barcode view writes "1.0 (43g)" here and a
       3.25rem box broke it over two lines. */
    flex: 0 0 auto;
    min-width: 3.25rem;
    white-space: nowrap;
    text-align: right;
}

/* The signed-in address, at the top of the avatar menu. */
.tk-menu__whose {
    margin: 0;
    padding: var(--tk-space-3) var(--tk-space-4) var(--tk-space-2);
    font-size: var(--tk-text-xs);
    color: var(--tk-ink-muted);
    border-bottom: 1px solid var(--tk-border);
    overflow-wrap: anywhere;
}
.tk-menu__whose:empty { display: none; }

/* Logging is a sheet now, so it gets the room the page cannot give it. */
.tk-sheet--log .tk-sheet__body { padding-top: 0; }

/* The diary's own way into the log sheet, under the list. */
.tk-addfood {
    display: block;
    width: 100%;
    padding: var(--tk-space-3);
    font-family: var(--tk-font);
    font-size: var(--tk-text-sm);
    font-weight: 700;
    color: var(--tk-accent);
    background-color: transparent;
    border: 1px dashed var(--tk-border-strong);
    border-radius: var(--tk-radius);
    cursor: pointer;
    min-height: var(--tk-touch, 44px);
}
.tk-addfood:hover { background-color: var(--tk-surface-sunken); }

/* The logging card's head. The main thing the app is for, so its title sits
   centred over the box like "Today's Log" over the list, with the hint icon
   beside it rather than a 2xl title with an icon hanging off the end. */
/* A footer whose every control is hidden is still a 49px strip of nothing.
   The coach's does exactly that while it is calibrating: both buttons carry
   .hidden and the bar stays. An unsupported :has() simply leaves it as it
   was, which is the behaviour this replaces. */
.modal-footer:not(:has(> :not(.hidden))) { display: none; }

/* The manual form, revealed inside the log sheet by the Manual route. Its
   Add button was landing flush against the bottom edge of the sheet. */
#manual-content { padding-bottom: var(--tk-space-4); }

/* A route that has opened something below it, rather than gone somewhere. */
.tk-route--on {
    border-color: var(--tk-accent);
    color: var(--tk-accent);
    background-color: var(--tk-accent-soft, var(--tk-surface-sunken));
}

/* Nothing here yet, said properly: a heading, the reason, and the way out.
   Used by the reports modal when a range has nothing logged in it. */
.tk-blank {
    display: grid;
    justify-items: center;
    gap: var(--tk-space-1);
    padding: var(--tk-space-4);
    text-align: center;
    background-color: var(--tk-surface-sunken);
    border: 1px dashed var(--tk-border);
    border-radius: var(--tk-radius-lg, var(--tk-radius));
}
.tk-blank__title {
    font-size: var(--tk-text-base);
    font-weight: 800;
    color: var(--tk-ink);
}
.tk-blank__body {
    font-size: var(--tk-text-sm);
    color: var(--tk-ink-secondary);
    max-width: 38ch;
}
.tk-blank__body:empty { display: none; }
.tk-blank .tk-addfood { max-width: 18rem; margin-top: var(--tk-space-3); }

/* The way out of an empty state, where there is more than one. Side by side
   when they fit, stacked when they do not; used by the coach's "log a day or
   two first". */
.tk-blank__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--tk-space-2);
    width: 100%;
    max-width: 24rem;
}
.tk-blank__actions > button {
    flex: 1 1 9rem;
    min-height: var(--tk-touch, 44px);
}

/* What Pro gets you, on the paywall. */
.tk-perks {
    width: 100%;
    margin: 0 0 var(--tk-space-5);
    padding: 0;
    list-style: none;
    display: grid;
    gap: var(--tk-space-2);
    text-align: left;
}
.tk-perks li {
    display: flex;
    align-items: flex-start;
    gap: var(--tk-space-2);
    font-size: var(--tk-text-sm);
    color: var(--tk-ink-secondary);
}
.tk-perks__tick {
    flex: 0 0 auto;
    color: var(--tk-good-ink, var(--tk-accent));
    font-weight: 800;
}

/* The coach, as a conversation. Each answer used to replace the last. */
.tk-chat { display: grid; gap: var(--tk-space-3); }
.tk-chat:empty { display: none; }
.tk-chat__you {
    justify-self: end;
    max-width: 85%;
    margin: 0;
    padding: var(--tk-space-2) var(--tk-space-3);
    border-radius: var(--tk-radius) var(--tk-radius) 4px var(--tk-radius);
    background-color: var(--tk-accent-soft);
    color: var(--tk-ink);
    font-size: var(--tk-text-sm);
}
.tk-chat__coach {
    justify-self: start;
    max-width: 100%;
    padding: var(--tk-space-3);
    border-radius: var(--tk-radius) var(--tk-radius) var(--tk-radius) 4px;
    background-color: var(--tk-surface-sunken);
    border: 1px solid var(--tk-border);
    font-size: var(--tk-text-sm);
}
.tk-chat__coach:empty::after {
    content: "…";
    color: var(--tk-ink-muted);
}

/* The ways in that are not typing: labelled, side by side, under the box. */
.tk-routes {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--tk-space-2);
    margin-top: var(--tk-space-3);
}
/* Icon beside the label, as the phone's routes: two-thirds the height of
   the icon-over-label tile it was, for the same two choices. */
.tk-route {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: var(--tk-space-2);
    padding: var(--tk-space-3) var(--tk-space-2);
    min-height: 48px;
    font-family: var(--tk-font);
    font-size: var(--tk-text-sm);
    font-weight: 700;
    color: var(--tk-ink-secondary);
    background-color: var(--tk-surface);
    border: 1px solid var(--tk-border);
    border-radius: var(--tk-radius);
    cursor: pointer;
    transition: background-color var(--tk-duration) var(--tk-ease),
                border-color var(--tk-duration) var(--tk-ease);
}
.tk-route svg { width: 20px; height: 20px; }
.tk-route:hover { background-color: var(--tk-surface); border-color: var(--tk-border-strong); }
.tk-route:active { transform: scale(0.98); }
/* The Pro dot the icon carried in the box, kept on the button. */
.tk-route--pro { position: relative; }
.tk-route--pro::after {
    content: "";
    position: absolute;
    top: 8px;
    right: 8px;
    width: 6px;
    height: 6px;
    border-radius: var(--tk-radius-full);
    background-color: var(--tk-accent);
}

/* The hint sits in the sheet's head, between the title and the close button. */
.tk-sheet--log .tk-sheet__head .info-tooltip-container {
    margin-right: auto;
    margin-left: var(--tk-space-2);
    display: inline-flex;
    align-items: center;
    color: var(--tk-ink-muted);
}

/* ---- Recipe view ---------------------------------------------------------- */

/* Under the recipe's name: kcal, the log's macro line, "per serving". */
.tk-recipe__macros {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: center;
    gap: var(--tk-space-2) var(--tk-space-3);
}
.tk-recipe__kcal {
    font-size: var(--tk-text-lg);
    font-weight: 800;
    color: var(--tk-ink);
    font-variant-numeric: tabular-nums;
}
.tk-recipe__kcal small {
    margin-left: 2px;
    font-size: var(--tk-text-xs);
    font-weight: 500;
    color: var(--tk-ink-muted);
}
.tk-recipe__per {
    flex-basis: 100%;
    text-align: center;
    font-size: var(--tk-text-xs);
    color: var(--tk-ink-muted);
}

/* The section headings and lists inside the recipe. The headings were bare
   h3s at body size; an ingredient list ran straight on from the word
   "Ingredients". The phone's layout: a small label, then rows. */
#recipe-content h3 {
    margin: var(--tk-space-4) 0 var(--tk-space-2);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--tk-ink-muted);
}
#recipe-content ul li {
    padding: var(--tk-space-2) 0;
    border-bottom: 1px solid var(--tk-border);
    font-size: var(--tk-text-sm);
    color: var(--tk-ink);
}
#recipe-content ul li:last-child { border-bottom: 0; }
#recipe-content ol li {
    padding: var(--tk-space-1) 0;
    font-size: var(--tk-text-sm);
    line-height: 1.5;
    color: var(--tk-ink);
}

/* ---- Manual entry --------------------------------------------------------- */

/* What the three boxes come to. The bars below it are .tk-impact. */
.tk-manual__kcal {
    margin: 0;
    font-size: var(--tk-text-sm);
    color: var(--tk-ink-secondary);
}
.tk-manual__kcal strong {
    color: var(--tk-ink);
    font-size: var(--tk-text-lg);
    font-weight: 800;
    font-variant-numeric: tabular-nums;
}

/* ---- What adding this does to today ------------------------------------ */

/* Four bars under the staged food: eaten so far in the series hue, what this
   adds in a lighter band of the same hue, and what would be left. The user's
   words: "see how much of my macros it will use before I click log". */
.tk-impact {
    margin-top: var(--tk-space-3);
    padding: var(--tk-space-3);
    border-radius: var(--tk-radius);
    background-color: var(--tk-surface);
}
.tk-impact__title {
    margin: 0 0 var(--tk-space-2);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--tk-ink-muted);
}
.tk-impact__rows { display: grid; gap: var(--tk-space-2); }
.tk-impact__row {
    display: grid;
    grid-template-columns: 4.2rem 1fr auto;
    align-items: center;
    gap: var(--tk-space-2);
    font-size: var(--tk-text-xs);
}
.tk-impact__label { font-weight: 600; color: var(--tk-ink); }
.tk-impact__track {
    position: relative;
    display: block;
    height: 6px;
    border-radius: var(--tk-radius-full);
    background-color: var(--tk-surface-sunken);
    overflow: hidden;
}
.tk-impact__now, .tk-impact__add {
    position: absolute; top: 0; bottom: 0; left: 0;
    border-radius: var(--tk-radius-full);
    transition: width var(--tk-duration) var(--tk-ease), left var(--tk-duration) var(--tk-ease);
}
.tk-impact__add { opacity: 0.45; }
.tk-impact__figure {
    min-width: 4.5rem;
    text-align: right;
    color: var(--tk-ink-secondary);
    font-variant-numeric: tabular-nums;
}
.tk-impact__row.is-over .tk-impact__figure { color: var(--tk-critical-ink); font-weight: 700; }
.tk-impact__row.is-over .tk-impact__add { opacity: 0.9; background-color: var(--tk-critical); }

/* ---- Result actions ---------------------------------------------------- */

/* Add is the point of the panel and was one of three equal-weight buttons at
   the end of a scrolling area. It takes the remaining width now, and the row
   sticks to the bottom so it is reachable without scrolling to the end of a
   long list of items. */
.tk-result__actions {
    display: flex;
    align-items: center;
    /* Wraps because a scan adds a fourth button: Cancel, Scan another, Save as
       Preset, Add do not fit one phone-width row. */
    flex-wrap: wrap;
    gap: var(--tk-space-2);
    margin-top: var(--tk-space-4);
    padding-top: var(--tk-space-3);
    border-top: 1px solid var(--tk-border);
    /* Pinned to the foot of the sheet. A staged meal of several items is
       taller than the sheet, and Add -- the only way to finish -- started
       24px below the fold, measured on the handset. Sticky keeps it on
       screen at every scroll position without shortening the panel. */
    position: sticky;
    bottom: 0;
    z-index: 2;
    margin-left: calc(-1 * var(--tk-space-4));
    margin-right: calc(-1 * var(--tk-space-4));
    padding-left: var(--tk-space-4);
    padding-right: var(--tk-space-4);
    padding-bottom: var(--tk-space-3);
    background-color: var(--tk-surface);
}

.tk-result__actions .tk-result__add { flex: 1 1 auto; min-width: 96px; }

/* In the log sheet the bar sits over the sheet body's bottom padding. At
   bottom: 0 it stopped 16px short of the edge -- the tray did the same -- and
   the impact block's last row ("Fat 55g left") was drawn underneath Add. */
#log-sheet .tk-result__actions {
    bottom: calc(-1 * var(--tk-space-4));
    padding-bottom: calc(var(--tk-space-3) + var(--tk-space-4));
}

@media (max-width: 380px) {
    .tk-macros { grid-template-columns: repeat(2, 1fr); }
}

/* ====================================================================== */
/* The More sheet                                                          */
/* ====================================================================== */

/* This was a 288px panel that slid in from the left edge -- a desktop
   navigation drawer, opened by a control in the bottom right. The eye had to
   cross the whole screen to find what the thumb had just summoned, and on a
   phone the panel covered the app without ever feeling attached to it.
   A sheet rises from the bar that opened it, so the motion starts where the
   finger already is. */

/* Slightly slower than the app's 160ms because this travels the height of a
   thumb reach, not a few pixels of colour. The curve is heavily front-loaded:
   it covers most of the distance immediately, which is what makes a sheet read
   as fast even though it takes longer than a fade. */
:root {
    --tk-sheet-duration: 300ms;
    --tk-sheet-ease: cubic-bezier(0.32, 0.72, 0, 1);
}

#menu-backdrop {
    position: fixed;
    inset: 0;
    /* Above the bottom bar (45) so the bar dims with everything else, below
       the sheet (50). */
    z-index: 46;
    background-color: rgba(10, 14, 20, 0.45);
    opacity: 0;
    transition: opacity var(--tk-sheet-duration) var(--tk-sheet-ease);
    -webkit-backdrop-filter: blur(2px);
    backdrop-filter: blur(2px);
}
#menu-backdrop.backdrop-visible { display: block; opacity: 1; }
#menu-backdrop.backdrop-hidden { opacity: 0; pointer-events: none; }

.tk-sheet {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 50;
    margin: 0 auto;
    max-width: 560px;
    max-height: min(86vh, 760px);
    display: flex;
    flex-direction: column;
    background-color: var(--tk-surface);
    border-top: 1px solid var(--tk-border);
    border-radius: 20px 20px 0 0;
    box-shadow: 0 -10px 40px rgba(10, 14, 20, 0.22);
    transform: translate3d(0, 100%, 0);
    transition: transform var(--tk-sheet-duration) var(--tk-sheet-ease),
                visibility 0s linear var(--tk-sheet-duration);
    /* Off-screen is not enough on its own: without this the sheet's buttons
       stay in the tab order and a screen reader still walks them. */
    visibility: hidden;
    overscroll-behavior: contain;
}

.tk-sheet.menu-visible {
    transform: translate3d(0, 0, 0);
    visibility: visible;
    transition: transform var(--tk-sheet-duration) var(--tk-sheet-ease),
                visibility 0s;
}

/* Set while a finger is on the sheet: the transform follows the drag directly
   and must not be smoothed, or it lags behind the finger. */
.tk-sheet.is-dragging {
    transition: none;
    visibility: visible;
}

.tk-sheet__grab {
    flex: 0 0 auto;
    display: flex;
    justify-content: center;
    padding: 10px 0 4px;
    /* The drag is handled in JS, so the browser must not claim the gesture
       for a scroll first. */
    touch-action: none;
    cursor: grab;
}
.tk-sheet__grab::before {
    content: "";
    width: 40px;
    height: 4px;
    border-radius: var(--tk-radius-full);
    background-color: var(--tk-border-strong);
}

.tk-sheet__head {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--tk-space-3);
    padding: 2px var(--tk-space-4) var(--tk-space-3);
    touch-action: none;
}

.tk-sheet__title {
    font-family: var(--tk-font);
    font-size: var(--tk-text-lg);
    font-weight: 700;
    color: var(--tk-ink);
    margin: 0;
}

.tk-sheet__close {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    flex: 0 0 auto;
    border: 0;
    border-radius: var(--tk-radius-full);
    background-color: var(--tk-surface-sunken);
    color: var(--tk-ink-secondary);
    cursor: pointer;
    transition: background-color var(--tk-duration) var(--tk-ease);
    -webkit-tap-highlight-color: transparent;
}
.tk-sheet__close:hover { color: var(--tk-ink); }
.tk-sheet__close svg { width: 17px; height: 17px; }

.tk-sheet__body {
    flex: 1 1 auto;
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    /* The home indicator sits over the last row otherwise. */
    padding: 0 var(--tk-space-4) calc(var(--tk-space-4) + env(safe-area-inset-bottom, 0px));
    display: flex;
    flex-direction: column;
    gap: var(--tk-space-3);
}

/* ---- The tool grid ---------------------------------------------------- */

/* Was seven full-width cards, each with an icon, a title, a subtitle and a
   chevron -- about 470px of scrolling for seven destinations. As a grid the
   same set is one glance and no scroll, and the subtitles are gone: nobody
   reads "Analyze your nutritional trends" twice. */
.tk-tools {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--tk-space-2);
}

.tk-tool {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 7px;
    min-height: 86px;
    padding: var(--tk-space-3) 6px;
    border: 1px solid var(--tk-border);
    border-radius: 14px;
    background-color: var(--tk-surface);
    color: var(--tk-ink);
    font-family: var(--tk-font);
    font-size: var(--tk-text-xs);
    font-weight: 600;
    line-height: 1.25;
    text-align: center;
    cursor: pointer;
    transition: transform 120ms var(--tk-ease),
                background-color 120ms var(--tk-ease);
    -webkit-tap-highlight-color: transparent;
}

.tk-tool:hover { background-color: var(--tk-surface-sunken); }
.tk-tool:active { transform: scale(0.96); }
.tk-tool:focus-visible { outline: 2px solid var(--tk-accent); outline-offset: 2px; }

.tk-tool__icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: var(--tk-radius-full);
    background-color: var(--tk-surface-sunken);
    color: var(--tk-ink-secondary);
}
.tk-tool__icon svg { width: 19px; height: 19px; }

/* Violet is the app's accent and is reserved here for the two AI surfaces, so
   the tint means "this one calls the model" rather than decorating the grid.
   Everything else stays neutral -- the three macro hues are spoken for by the
   data and must not be spent on menu chrome. */
.tk-tool--ai .tk-tool__icon {
    background-color: var(--tk-accent-soft);
    color: var(--tk-accent);
}

/* Labels are the full translation keys ("Track Progress", not "Progress"):
   i18n rewrites textContent from the key whenever the dictionary has no entry,
   so a shortened label would be replaced by the long one at runtime. They wrap
   to two lines; the tile height is fixed so a mixed grid still lines up. */
.tk-tool__label {
    display: block;
    max-width: 100%;
    hyphens: auto;
}

/* The stale-weight dot. It used to sit in a row of chevrons where it read as
   punctuation; on a tile corner it reads as a notification. */
.tk-tool .tk-tool__badge {
    position: absolute;
    top: 8px;
    right: 8px;
    width: 8px;
    height: 8px;
    border-radius: var(--tk-radius-full);
    background-color: var(--tk-critical);
}

/* An open accordion pane breaks the grid and takes the full width, directly
   under the tile that opened it. The pane must stay the tile's immediate
   sibling -- `.tab-btn.active + .accordion-content` depends on it. */
.tk-tools > .tab-pane {
    grid-column: 1 / -1;
    background-color: var(--tk-surface-sunken);
    border-radius: 12px;
    margin-top: 0;
}

/* The tile is the accordion header now, so the old header styling -- a filled
   bar with square bottom corners -- no longer applies. */
.tk-tools > .tab-btn.active {
    background-color: var(--tk-surface-sunken);
    border-radius: 14px;
}

/* These three tiles still carry `.tab-btn` from the accordion era. The inline
   block in index.html used to grey them with `.dark .tab-btn`, which weighs the
   same (0,2,0) as the rule below and sits later in source order -- so this line
   lost, and three working tools read as disabled for as long as both existed.
   That rule is gone now; this stays as the explicit statement that a tool tile
   takes ink like its five neighbours. */
.tk-tools > .tab-btn { color: var(--tk-ink); }

/* ---- Full-width rows -------------------------------------------------- */

.tk-sheet__row {
    display: flex;
    align-items: center;
    gap: var(--tk-space-3);
    width: 100%;
    min-height: 56px;
    padding: var(--tk-space-3);
    border: 1px solid var(--tk-border);
    border-radius: 14px;
    background-color: var(--tk-surface);
    color: var(--tk-ink);
    font-family: var(--tk-font);
    text-align: left;
    cursor: pointer;
    transition: background-color 120ms var(--tk-ease);
    -webkit-tap-highlight-color: transparent;
}
.tk-sheet__row:hover { background-color: var(--tk-surface-sunken); }
.tk-sheet__row:focus-visible { outline: 2px solid var(--tk-accent); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
    .tk-sheet { transition: visibility 0s; }
    .tk-sheet.menu-visible { transition: visibility 0s; }
    #menu-backdrop { transition: none; }
    .tk-tool { transition: none; }
}

.tk-skeleton {
    background: linear-gradient(90deg, var(--tk-surface-sunken) 25%, var(--tk-border) 50%, var(--tk-surface-sunken) 75%);
    background-size: 200% 100%;
    animation: tk-shimmer 1.4s infinite;
    border-radius: var(--tk-radius-sm);
}

@keyframes tk-shimmer {
    from { background-position: 200% 0; }
    to { background-position: -200% 0; }
}

@media (prefers-reduced-motion: reduce) {
    .tk-skeleton { animation: none; }
}

/* Visually hidden but available to screen readers. */
.tk-sr-only {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* ====================================================================== */
/* Newly logged rows                                                       */
/* ====================================================================== */

/* The food log re-renders wholesale on every change, so this is applied by
   updateFoodLogUI to the one row that was just added. Without it a logged
   item simply appears, which was the main reason the dashboard read as
   static: the most frequent action in the app had no visible result.

   Kept to opacity and transform so it stays on the compositor and cannot
   reflow the list underneath it. */
@keyframes tk-row-enter {
    from { opacity: 0; transform: translateY(-6px); }
    to   { opacity: 1; transform: none; }
}

.tk-row-enter {
    animation: tk-row-enter calc(var(--tk-duration) * 2) var(--tk-ease) both;
}

@media (prefers-reduced-motion: reduce) {
    .tk-row-enter { animation: none; }
}

/* ====================================================================== */
/* Catalogue tile icons                                                    */
/* ====================================================================== */

/* The glyphs in js/catalogue.js carry geometry and a colour class; the palette
   lives here so it stays themeable and every tile is drawn the same way.
   Colour is what makes a food icon readable at 40px -- a red circle with a
   green leaf is an apple instantly, where the same circle in line art is just
   a circle. Shapes stay simple and the palette does the identifying work.

   These hues are decorative, not data: unlike the macro colours they never
   encode a value, so they are chosen for food plausibility and legibility on
   both surfaces rather than for all-pairs colour-blind separation. */
:root {
    --tk-food-red:    #e2564a;
    --tk-food-crimson:#b5433f;
    --tk-food-orange: #ec8b3c;
    --tk-food-amber:  #efb52f;
    --tk-food-green:  #56a44b;
    --tk-food-olive:  #86a83f;
    --tk-food-blue:   #4c92d6;
    --tk-food-purple: #8a5fc0;
    --tk-food-pink:   #ef8f9c;
    --tk-food-brown:  #a06f45;
    --tk-food-cocoa:  #6f4a2e;
    --tk-food-cream:  #ecdcb9;
    --tk-food-white:  #f4f1ea;
    --tk-food-slate:  #93a3b4;
    /* Warm and dark enough to hold a pale silhouette against a white tile. */
    --tk-food-outline: rgba(92, 74, 44, 0.55);
}

/* Lifted a little on the dark surface so the deeper hues keep their shape. */
html.dark {
    --tk-food-red:    #ef6a5c;
    --tk-food-crimson:#d1554e;
    --tk-food-orange: #f59a4c;
    --tk-food-amber:  #f6c342;
    --tk-food-green:  #64b857;
    --tk-food-olive:  #9bbd4c;
    --tk-food-blue:   #5ea3e6;
    --tk-food-purple: #9d73d2;
    --tk-food-pink:   #f5a0ac;
    --tk-food-brown:  #b6804f;
    --tk-food-cocoa:  #86593a;
    --tk-food-cream:  #f0e3c6;
    --tk-food-white:  #f8f6f1;
    --tk-food-slate:  #a4b3c3;
    /* On the dark surface the pale fills already separate; the outline only
       needs to define the edge, so it is a soft shadow rather than a line. */
    --tk-food-outline: rgba(0, 0, 0, 0.35);
}

.tk-tile__icon svg { overflow: visible; }

/* Fills. Shapes name a hue; nothing in the markup names a literal colour. */
.tk-tile__icon .f-red     { fill: var(--tk-food-red); }
.tk-tile__icon .f-crim    { fill: var(--tk-food-crimson); }
.tk-tile__icon .f-org     { fill: var(--tk-food-orange); }
.tk-tile__icon .f-amb     { fill: var(--tk-food-amber); }
.tk-tile__icon .f-grn     { fill: var(--tk-food-green); }
.tk-tile__icon .f-oli     { fill: var(--tk-food-olive); }
.tk-tile__icon .f-blu     { fill: var(--tk-food-blue); }
.tk-tile__icon .f-pur     { fill: var(--tk-food-purple); }
.tk-tile__icon .f-pnk     { fill: var(--tk-food-pink); }
.tk-tile__icon .f-brn     { fill: var(--tk-food-brown); }
.tk-tile__icon .f-coc     { fill: var(--tk-food-cocoa); }
.tk-tile__icon .f-crm     { fill: var(--tk-food-cream); }
.tk-tile__icon .f-wht     { fill: var(--tk-food-white); }
.tk-tile__icon .f-sla     { fill: var(--tk-food-slate); }

/* Pale foods are genuinely pale -- a chicken bone, an egg white, milk, tofu --
   and against a light surface they disappear: cream measured 1.14 contrast on
   the light icon disc, white 1.05, so the drumstick read as meat with nothing
   attached. Deepening the disc does not rescue them, because cream sits at
   mid-luminance and darkening the ground costs the amber foods more than it
   gains here. An outline fixes the silhouette instead, on any background and
   without repainting the food a colour it is not.

   paint-order puts the stroke behind the fill, so it shows as a clean outer
   edge rather than eating into the shape. */
.tk-tile__icon .f-crm,
.tk-tile__icon .f-wht {
    stroke: var(--tk-food-outline);
    stroke-width: 0.9;
    paint-order: stroke fill;
}

/* Two neutral overlays give every icon depth without doubling the palette:
   a shade that darkens whatever is beneath it and a highlight that lifts it. */
.tk-tile__icon .sh { fill: rgba(0, 0, 0, 0.17); }
.tk-tile__icon .hl { fill: rgba(255, 255, 255, 0.30); }
.tk-tile__icon .ink { fill: rgba(38, 28, 20, 0.62); }

/* Strokes, for the few forms that read better as a line than a filled shape. */
.tk-tile__icon .s {
    fill: none;
    stroke-width: 1.5;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.tk-tile__icon .s-grn { stroke: var(--tk-food-green); }
.tk-tile__icon .s-brn { stroke: var(--tk-food-brown); }
.tk-tile__icon .s-crm { stroke: var(--tk-food-cream); }
.tk-tile__icon .s-org { stroke: var(--tk-food-orange); }
.tk-tile__icon .s-amb { stroke: var(--tk-food-amber); }
.tk-tile__icon .s-sla { stroke: var(--tk-food-slate); }
.tk-tile__icon .s-ink { stroke: rgba(38, 28, 20, 0.55); }

/* The tile lifts its icon slightly on press, which reads as the tap landing. */
.tk-tile__icon svg { transition: transform var(--tk-duration) var(--tk-ease); }
.tk-tile:active .tk-tile__icon svg { transform: scale(0.92); }

/* ====================================================================== */
/* AI results arriving                                                     */
/* ====================================================================== */

/* An AI answer lands after several seconds of waiting and used to appear
   between one frame and the next, which reads as a jump rather than an
   arrival. Opacity and transform only, so it stays on the compositor. */
@keyframes tk-reveal {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
}

.tk-reveal {
    animation: tk-reveal calc(var(--tk-duration) * 2.5) var(--tk-ease) both;
}

@media (prefers-reduced-motion: reduce) {
    .tk-reveal { animation: none; }
}

/* ====================================================================== */
/* Meal suggestion cards                                                   */
/* ====================================================================== */

/* These were solid #6366f1 -- a hue that appears nowhere in the palette,
   introduced with the comment "a nice purple, similar to the screenshot" --
   with white text, and the macro pills stranded on the page underneath rather
   than on the card they belonged to.
   The wait was worse than the colour. Each card was greyed to opacity-50,
   given a `cursor-not-allowed`, and covered by a black 40% overlay with a
   spinner in it while its recipe pre-fetched in the background. Three cards
   mid-load read as three failures, and the one card that genuinely had failed
   looked identical to the two that were merely slow.
   Now the card arrives complete -- title, description and macros are all known
   from the suggestions call -- and only the footer, which is the part actually
   waiting, shows it. */
.tk-mealcard {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--tk-space-2);
    width: 100%;
    text-align: left;
    padding: var(--tk-space-4);
    background-color: var(--tk-surface);
    border: 1px solid var(--tk-border);
    border-radius: var(--tk-radius-lg);
    box-shadow: var(--tk-shadow-sm);
    font-family: var(--tk-font);
    cursor: pointer;
    transition: border-color var(--tk-duration) var(--tk-ease),
                box-shadow var(--tk-duration) var(--tk-ease),
                transform var(--tk-duration) var(--tk-ease);
}

.tk-mealcard:hover {
    border-color: var(--tk-border-strong);
    box-shadow: var(--tk-shadow);
    transform: translateY(-2px);
}

.tk-mealcard:focus-visible {
    outline: 2px solid var(--tk-accent);
    outline-offset: 2px;
}

.tk-mealcard:active { transform: translateY(0); }

.tk-mealcard__title {
    margin: 0;
    font-size: var(--tk-text-base);
    font-weight: 650;
    line-height: 1.3;
    color: var(--tk-ink);
}

.tk-mealcard__desc {
    margin: 0;
    font-size: var(--tk-text-sm);
    line-height: 1.45;
    color: var(--tk-ink-secondary);
}

.tk-mealcard__macros {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: var(--tk-space-2);
    margin-top: auto;
    padding-top: var(--tk-space-2);
    font-size: var(--tk-text-xs);
    font-variant-numeric: tabular-nums;
}
.tk-mealcard__kcal { font-size: var(--tk-text-base); font-weight: 800; color: var(--tk-ink); margin-right: var(--tk-space-1); }
.tk-mealcard__kcal small { margin-left: 2px; font-size: var(--tk-text-xs); font-weight: 500; color: var(--tk-ink-muted); }

/* The footer is the only part that waits, so it is the only part that moves.
   A bar the width of the words that are coming, rather than a spinner: it
   says the recipe is being written, not that the app is thinking. */
.tk-mealcard__foot {
    display: flex;
    align-items: center;
    gap: var(--tk-space-2);
    min-height: 1.15rem;
    font-size: var(--tk-text-xs);
    font-weight: 600;
    color: var(--tk-ink-muted);
}

.tk-mealcard__foot .tk-skeleton {
    display: block;
    height: 0.6rem;
    width: 8.5rem;
    max-width: 70%;
}

/* Waiting is not disabled. The card stays legible and stays the same size --
   only the pointer says the recipe is not there yet, so nothing shifts when
   it arrives. */
.tk-mealcard[aria-busy="true"] { cursor: progress; }

.tk-mealcard--failed {
    border-color: var(--tk-critical);
}

.tk-mealcard--failed .tk-mealcard__foot { color: var(--tk-critical-ink); }

.tk-mealcard__foot svg { width: 0.9rem; height: 0.9rem; flex-shrink: 0; }

/* The row holding "Tap one for a recipe" and Regenerate. Regenerate belongs
   beside the results it replaces, not back up in the filters -- it is a
   reaction to what is on screen. */
.tk-suggest-actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--tk-space-3);
    margin-bottom: var(--tk-space-4);
}

.tk-suggest-actions__hint {
    font-size: var(--tk-text-sm);
    color: var(--tk-ink-muted);
    margin: 0;
}

/* Skeleton cards for the first wait, when there are no titles yet to show.
   Same footprint as the real card, so the grid does not jump when they swap. */
.tk-mealcard--skeleton {
    cursor: default;
    gap: var(--tk-space-3);
}

.tk-mealcard--skeleton:hover {
    transform: none;
    border-color: var(--tk-border);
    box-shadow: var(--tk-shadow-sm);
}

.tk-mealcard--skeleton .tk-skeleton { display: block; height: 0.7rem; }
.tk-mealcard--skeleton .tk-skeleton:nth-child(1) { height: 0.95rem; width: 75%; }
.tk-mealcard--skeleton .tk-skeleton:nth-child(2) { width: 100%; }
.tk-mealcard--skeleton .tk-skeleton:nth-child(3) { width: 60%; }
.tk-mealcard--skeleton .tk-skeleton:nth-child(4) { width: 45%; margin-top: auto; }

/* The recipe view's macro strip. Was bg-indigo-50 / text-indigo-600 with a
   dark:bg-slate-700 beside it -- two hardcoded palettes for one element. */
.tk-recipe-controls {
    background-color: var(--tk-surface-sunken);
    border: 1px solid var(--tk-border);
    border-radius: var(--tk-radius);
    padding: var(--tk-space-3);
}

.tk-recipe-controls__macros {
    margin: 0;
    font-weight: 700;
    color: var(--tk-ink);
}

.tk-recipe-controls__label {
    margin: 0;
    font-size: var(--tk-text-xs);
    font-weight: 500;
    color: var(--tk-ink-muted);
}

/* The recipe detail wait. Rows of decreasing certainty: a heading, the
   ingredients, a heading, the method. */
.tk-recipe-skeleton {
    display: flex;
    flex-direction: column;
    gap: var(--tk-space-3);
    padding: var(--tk-space-2) 0;
}

.tk-recipe-skeleton > span {
    display: block;
    height: 0.7rem;
    width: 100%;
}

.tk-recipe-skeleton__head {
    height: 1rem;
    width: 35%;
    margin-top: var(--tk-space-3);
}

.tk-recipe-skeleton > span:nth-child(4) { width: 72%; }
.tk-recipe-skeleton > span:nth-child(8) { width: 88%; }
.tk-recipe-skeleton > span:last-child { width: 55%; }

/* The second way in. Shorter than the camera drop above it, because it is the
   alternative rather than the offer -- the camera is what the scanner opened
   for, and this should read as a way out of that, not a rival to it. */
.tk-photodrop--slim {
    min-height: 0;
    flex-direction: row;
    gap: var(--tk-space-3);
    padding: var(--tk-space-3) var(--tk-space-4);
    border-style: solid;
    border-width: 1px;
    border-color: var(--tk-border);
    background-color: transparent;
    font-weight: 500;
}

.tk-photodrop--slim svg { width: 20px; height: 20px; }

/* ====================================================================== */
/* Dashboard                                                              */
/* ====================================================================== */

/* The screen was one card holding 99 elements: two rows of chrome before any
   data, four progress bars of near-equal weight, four more behind an
   accordion, and a second column of trackers competing with the macros.
   Nothing outranked anything, so everything had to be read.
   It is a stack of small cards now -- one answer, three supporting numbers,
   and the rest demoted until it has something to say. */
.tk-dash {
    display: grid;
    /* minmax(0, 1fr), not the implicit auto track: an auto track takes the
       widest item's min-content, and the account line under the title is
       nowrap -- so a long e-mail made the track 460px on a 411px phone and
       every card in the column ran off the right edge. Seen on the handset,
       not in the desktop emulation, where the test account's name was short. */
    grid-template-columns: minmax(0, 1fr);
    gap: var(--tk-space-3);
    background: none;
    border: 0;
    box-shadow: none;
    padding: 0;
}

/* The three the phone's Today has carried and the browser's had not: what is
   left of the day's calories, whether the account is paying, and which account
   it is. Sized against the figures they sit under rather than invented. */
.tk-hero__remaining {
    margin-top: var(--tk-space-2);
    font-size: var(--tk-text-xs);
    color: var(--tk-ink-muted);
}

.tk-dash__pro {
    margin-left: var(--tk-space-2);
    padding: 2px 8px;
    border-radius: var(--tk-radius-full);
    background-color: var(--tk-accent-soft);
    color: var(--tk-accent);
    font-size: var(--tk-text-xs);
    font-weight: 800;
    letter-spacing: 0.04em;
    vertical-align: middle;
}

.tk-dash__whose {
    margin: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--tk-text-xs);
    color: var(--tk-ink-muted);
}

.tk-dash__whose:empty { display: none; }

/* The weekly review, as a card rather than the modal it used to be. Sits
   above the figures, the way the phone's does. */
.tk-review__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--tk-space-2);
}

.tk-review__title {
    font-size: var(--tk-text-base);
    font-weight: 700;
    color: var(--tk-ink);
}

/* Which week the review is about, under its heading. */
.tk-review__range {
    margin-top: 2px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    color: var(--tk-ink-muted);
}

.tk-review__close {
    flex: none;
    width: 44px;
    height: 44px;
    margin: -10px -10px 0 0;
    font-size: 22px;
    line-height: 1;
    color: var(--tk-ink-muted);
}

.tk-review__close:hover { color: var(--tk-ink); }

.tk-review__body {
    margin-top: var(--tk-space-2);
    font-size: var(--tk-text-sm);
    color: var(--tk-ink-secondary);
}

.tk-dash__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--tk-space-3);
    padding: var(--tk-space-1) var(--tk-space-1) 0;
}
.tk-dash__lead { flex: 1 1 auto; min-width: 0; }
.tk-dash__when { flex: 0 0 auto; }
.tk-dash__titlerow { display: flex; align-items: center; }

.tk-dash__title {
    margin: 0;
    font-size: var(--tk-text-xl);
    font-weight: 700;
    letter-spacing: -0.015em;
    color: var(--tk-ink);
}

.tk-dash__when { display: flex; align-items: center; gap: var(--tk-space-2); }

.tk-dash__picker {
    padding: 0.3rem 0.5rem;
    font-size: var(--tk-text-xs);
    font-family: var(--tk-font);
    color: var(--tk-ink-secondary);
    background-color: var(--tk-surface);
    border: 1px solid var(--tk-border);
    border-radius: var(--tk-radius-sm);
}

/* The day-stepping arrows, the phone's < >. 36px so the row stays a row
   beside the picker on a 411px phone; the touch target is the row height. */
.tk-dash__step {
    width: 32px;
    min-height: 32px;
    padding: 0;
    font-size: var(--tk-text-lg);
    line-height: 1;
    font-family: var(--tk-font);
    color: var(--tk-ink-secondary);
    background-color: var(--tk-surface);
    border: 1px solid var(--tk-border);
    border-radius: var(--tk-radius-full);
    cursor: pointer;
}
.tk-dash__step:hover { color: var(--tk-ink); border-color: var(--tk-border-strong); }
.tk-dash__step--cal { display: inline-flex; align-items: center; justify-content: center; }
.tk-dash__step--cal svg { width: 17px; height: 17px; }
/* Kept in the document for its change event and showPicker(); not seen. */
.tk-dash__picker--hidden { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.tk-dash__step[disabled] { opacity: 0.35; cursor: default; }

.tk-dash__today {
    padding: 0.3rem 0.6rem;
    font-size: var(--tk-text-xs);
    font-weight: 600;
    font-family: var(--tk-font);
    color: var(--tk-ink-secondary);
    background-color: var(--tk-surface);
    border: 1px solid var(--tk-border);
    border-radius: var(--tk-radius-sm);
    cursor: pointer;
    transition: color var(--tk-duration) var(--tk-ease),
                border-color var(--tk-duration) var(--tk-ease);
}
.tk-dash__today:hover { color: var(--tk-ink); border-color: var(--tk-border-strong); }

/* ---- Banners -------------------------------------------------------- */
/* Were bg-blue-100 / bg-red-100 with a border-l-4: four hardcoded colours
   that existed in no palette and did not change in dark mode. */
.tk-note {
    display: flex;
    align-items: flex-start;
    gap: var(--tk-space-3);
    padding: var(--tk-space-3) var(--tk-space-4);
    background-color: var(--tk-surface);
    border: 1px solid var(--tk-border);
    border-left: 3px solid var(--tk-accent);
    border-radius: var(--tk-radius);
    color: var(--tk-ink-secondary);
    font-size: var(--tk-text-sm);
}

.tk-note--alert { border-left-color: var(--tk-warning); }
.tk-note__icon { width: 20px; height: 20px; flex-shrink: 0; color: var(--tk-ink-muted); }
.tk-note__text { margin: 0; flex: 1 1 auto; }
.tk-note__text p { margin: 0; }
.tk-note__title { font-weight: 650; color: var(--tk-ink); }
.tk-note__link { color: var(--tk-accent); font-weight: 650; text-decoration: underline; cursor: pointer; }
.tk-note__close {
    flex-shrink: 0; border: 0; background: none; cursor: pointer;
    color: var(--tk-ink-muted); padding: 0; line-height: 0;
}
.tk-note__close:hover { color: var(--tk-ink); }

/* ---- Hero ------------------------------------------------------------ */
.tk-hero { display: grid; gap: var(--tk-space-3); }
.tk-hero__row { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--tk-space-3); }
.tk-hero__figure { display: grid; gap: 0.15rem; min-width: 0; }

.tk-hero__label {
    font-size: var(--tk-text-xs);
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--tk-ink-muted);
}

/* Tabular figures because this number counts up on every log, and
   proportional digits make it jitter sideways while it animates. */
.tk-hero__value {
    font-size: var(--tk-text-hero);
    font-weight: 800;
    line-height: 1;
    letter-spacing: -0.035em;
    color: var(--tk-ink);
    font-variant-numeric: tabular-nums;
    cursor: pointer;
}

/* ---- Macro rows ------------------------------------------------------ */
/* Named tk-dmacro, not tk-macro. The shorter name was already taken by the
   AI logging card (components.css:1464), including a media query that forces
   its children into two columns -- so the dashboard's three rows inherited a
   grid meant for something else and rendered as cramped boxes with the
   figures wrapping mid-value. Seen on a real phone, not in any measurement I
   took: getComputedStyle on a hidden #app-content reports the rules that
   match, and both sets matched. */
.tk-dmacros { display: grid; gap: var(--tk-space-4); }
.tk-dmacro { display: grid; gap: 0.35rem; }
.tk-dmacro__top { display: flex; align-items: baseline; justify-content: space-between; gap: var(--tk-space-2); }
.tk-dmacro__name { font-size: var(--tk-text-sm); font-weight: 600; color: var(--tk-ink); }
.tk-dmacro__value {
    font-size: var(--tk-text-sm);
    color: var(--tk-ink-secondary);
    font-variant-numeric: tabular-nums;
    cursor: pointer;
}

.tk-track {
    height: 5px;
    background-color: var(--tk-surface-sunken);
    border-radius: var(--tk-radius-full);
    overflow: hidden;
}

.tk-track__fill {
    height: 100%;
    width: 0;
    border-radius: var(--tk-radius-full);
    transition: width calc(var(--tk-duration) * 3) var(--tk-ease);
}

/* The micros are not a fourth, fifth, sixth and seventh series -- the palette
   carries three hues on purpose. They stay neutral until one of them is worth
   looking at, and then the chip above says which. */
.tk-track__fill--quiet { background-color: var(--tk-nutrient-neutral); }

.tk-hero .tk-track { height: 7px; }

/* The macros and the micronutrient row live in the hero card now, each
   behind a hairline. Three cards for one set of numbers cost two lots of
   padding and two gaps, and put the food box a screen down on a phone. */
.tk-hero { padding: var(--tk-space-4); grid-template-columns: minmax(0, 1fr); }
.tk-hero .tk-dmacros {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    margin-top: var(--tk-space-3);
    padding-top: var(--tk-space-3);
    border-top: 1px solid var(--tk-border);
    gap: var(--tk-space-3);
}
/* Side by side: the name over the figure, the bar under both. Three full
   rows were 110px on a phone for three numbers. */
.tk-hero .tk-dmacros .tk-dmacro { gap: var(--tk-space-1); }
.tk-hero .tk-dmacros .tk-dmacro__top { flex-direction: column; align-items: flex-start; gap: 0; }
.tk-hero .tk-dmacros .tk-dmacro__value { font-size: var(--tk-text-xs); white-space: nowrap; }
.tk-hero .tk-dmacros .tk-track { height: 5px; }
.tk-hero .tk-micros {
    margin-top: var(--tk-space-3);
    padding: var(--tk-space-2) 0 0;
    border-top: 1px solid var(--tk-border);
}
.tk-hero__row { align-items: baseline; }
/* The foot of the card: the micronutrients toggle on the left, the water
   tile on the right, and the micronutrient bars open full-width beneath. */
.tk-hero__foot { display: flex; align-items: center; gap: var(--tk-space-3); }
.tk-hero__foot .tk-micros__head { flex: 1 1 auto; min-width: 0; min-height: 40px; }
.tk-hero .tk-strip { display: flex; gap: var(--tk-space-2); flex: 0 0 auto; }
.tk-hero .tk-tracker {
    padding: var(--tk-space-1) var(--tk-space-3);
    min-height: 40px;
    box-shadow: none;
    border-radius: var(--tk-radius-full);
    gap: var(--tk-space-2);
}
.tk-hero .tk-tracker__key { display: none; }
.tk-hero .tk-tracker__icon svg { width: 16px; height: 16px; }
.tk-hero .tk-tracker__value { font-size: var(--tk-text-base); }
.tk-hero .tk-tracker__value--time { font-size: var(--tk-text-sm); }
.tk-hero .tk-hero__value { font-size: 2.25rem; }
.tk-hero .tk-hero__remaining { margin: 0; text-align: right; flex: 0 0 auto; }
@media (min-width: 640px) {
    .tk-hero { padding: var(--tk-space-5); }
    .tk-hero .tk-dmacros { grid-template-columns: 1fr; }
    .tk-hero .tk-dmacros .tk-dmacro__top { flex-direction: row; align-items: baseline; }
    .tk-hero .tk-dmacros .tk-dmacro__value { font-size: var(--tk-text-sm); }
    .tk-hero .tk-hero__value { font-size: var(--tk-text-hero); }
}

/* The sentence under the micros that says where a near-limit one came from. */
.tk-micros__source {
    margin: 0;
    font-size: var(--tk-text-xs);
    color: var(--tk-ink-muted);
}

/* ---- Micronutrients -------------------------------------------------- */
/* The head is already 44px -- a full touch target -- so the card's own
   vertical padding was stacking a second gap on top of one that was there for
   the finger. Trimmed so the collapsed row sits level with the water row
   beneath it rather than standing 23px taller than it for no reason. */
.tk-micros { display: grid; padding: var(--tk-space-1) var(--tk-space-4); }
.tk-micros__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--tk-space-2);
    width: 100%;
    padding: 0;
    border: 0;
    background: none;
    font-family: var(--tk-font);
    color: var(--tk-ink);
    cursor: pointer;
}
.tk-micros__head:focus-visible { outline: 2px solid var(--tk-accent); outline-offset: 3px; border-radius: var(--tk-radius-sm); }
.tk-micros__label { font-size: var(--tk-text-sm); font-weight: 600; }
.tk-micros__right { display: flex; align-items: center; gap: var(--tk-space-2); }
.tk-micros__chev {
    width: 14px; height: 14px; color: var(--tk-ink-muted);
    transition: transform calc(var(--tk-duration) * 2) var(--tk-ease);
}
.tk-micros__body { display: grid; gap: var(--tk-space-4); padding-top: var(--tk-space-4); }

/* Form as well as number: a shape appears at all only when something is near
   or past its limit, so a normal day has nothing extra to read. */
.tk-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.15rem 0.45rem;
    border: 1px solid currentColor;
    border-radius: var(--tk-radius-full);
    font-size: var(--tk-text-xs);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.tk-chip::before { content: ""; width: 5px; height: 5px; border-radius: 999px; background: currentColor; }
.tk-chip--warn { color: var(--tk-warning-ink); }
.tk-chip--over { color: var(--tk-critical-ink); }

/* ---- Water and the fast ---------------------------------------------- */
/* Stacked rows, not a side-by-side strip.
   The strip was designed against a mockup showing water beside the fasting
   timer, which balanced them -- but the fast is hidden unless one is running,
   so the real, normal state is water ALONE at full width with its content
   jammed against the left edge. Measured at 480px: a 448px card holding 130px
   of content, 68% of it empty. It read exactly as wide and clunky as it was.
   Now each tracker is a row of the same shape as the micronutrients row above
   it: what it is on the left, what it says on the right. */
.tk-strip { display: grid; gap: var(--tk-space-2); }

.tk-tracker {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--tk-space-3);
    padding: var(--tk-space-3) var(--tk-space-4);
    overflow: hidden;
    isolation: isolate;
    cursor: pointer;
    min-width: 0;
}

.tk-tracker__lead {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: center;
    gap: var(--tk-space-3);
    min-width: 0;
}

.tk-tracker__amount {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: baseline;
    gap: 0.3rem;
}

/* The target, stated. A count with nothing to measure it against was the other
   half of why the card looked empty -- there was nothing on the right to put
   there. */
.tk-tracker__goal {
    font-size: var(--tk-text-sm);
    font-weight: 600;
    color: var(--tk-ink-muted);
    font-variant-numeric: tabular-nums;
}
.tk-tracker:focus-visible { outline: 2px solid var(--tk-accent); outline-offset: 2px; }

.tk-tracker__fill {
    position: absolute;
    bottom: 0; left: 0; width: 100%;
    background-color: var(--tk-protein-soft);
    z-index: 0;
    transition: height calc(var(--tk-duration) * 4) var(--tk-ease);
}
.tk-tracker__fill--quiet { background-color: var(--tk-surface-sunken); }

.tk-tracker__icon { position: relative; z-index: 1; line-height: 0; color: var(--tk-protein); border-radius: var(--tk-radius-full); }
.tk-tracker__icon svg { width: 20px; height: 20px; }
#fasting-tracker .tk-tracker__icon { color: var(--tk-ink-muted); }

.tk-tracker__key {
    font-size: var(--tk-text-xs);
    font-weight: 600;
    color: var(--tk-ink-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.tk-tracker__value {
    font-size: var(--tk-text-base);
    font-weight: 700;
    line-height: 1.1;
    color: var(--tk-ink);
    font-variant-numeric: tabular-nums;
}
.tk-tracker__value--time { font-size: var(--tk-text-base); }

.tk-tracker__dot {
    position: absolute;
    top: -2px; right: -2px;
    width: 8px; height: 8px;
    border-radius: var(--tk-radius-full);
    background-color: var(--tk-ink-muted);
    border: 2px solid var(--tk-surface);
}

/* Room to breathe once there is room to breathe. */
@media (min-width: 768px) {
    .tk-dash { gap: var(--tk-space-4); }
    .tk-hero .tk-hero__value { font-size: calc(var(--tk-text-hero) * 1.15); }
    .tk-dash__title { font-size: var(--tk-text-2xl); }
}

/* ====================================================================== */
/* Goal wizard                                                            */
/* ====================================================================== */

/* The wizard carried 288 hardcoded Tailwind colour classes across 42 distinct
   names -- more than the rest of the app's screens put together -- and its
   accent was indigo, a hue in no palette. Selection was expressed four times
   in parallel: two class strings in the template, a third rebuilt on the icon
   by JS, and a fourth toggled on the checkmark. It is expressed once now, as
   aria-checked, and everything below reads it. */

.tk-option {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--tk-space-3);
    width: 100%;
    padding: var(--tk-space-3);
    background-color: var(--tk-surface);
    border: 1px solid var(--tk-border);
    border-radius: var(--tk-radius-lg);
    color: var(--tk-ink-secondary);
    cursor: pointer;
    text-align: left;
    transition: border-color var(--tk-duration) var(--tk-ease),
                background-color var(--tk-duration) var(--tk-ease),
                color var(--tk-duration) var(--tk-ease);
}

.tk-option:hover { border-color: var(--tk-border-strong); }
.tk-option:focus-visible { outline: 2px solid var(--tk-accent); outline-offset: 2px; }

/* Violet is the app's accent -- reserved for focus, links and Pro -- and it is
   deliberately far from all three macro hues, so a chosen card cannot be read
   as a macro. The border thickens rather than a ring being added: a ring on a
   1px border is two edges describing one boundary. */
.tk-option[aria-checked="true"] {
    background-color: var(--tk-accent-soft);
    border-color: var(--tk-accent);
    color: var(--tk-ink);
}

.tk-option__icon {
    flex-shrink: 0;
    display: grid;
    place-items: center;
    width: 32px;
    height: 32px;
    border-radius: var(--tk-radius-full);
    background-color: var(--tk-surface-sunken);
    color: var(--tk-ink-muted);
    transition: background-color var(--tk-duration) var(--tk-ease),
                color var(--tk-duration) var(--tk-ease);
}
.tk-option__icon svg { width: 18px; height: 18px; }
.tk-option[aria-checked="true"] .tk-option__icon {
    background-color: var(--tk-accent);
    color: var(--tk-action-ink);
}
/* --tk-action-ink follows the theme's action colour, which inverts in dark
   mode; the accent does not. Stated explicitly so the icon stays legible. */
html.dark .tk-option[aria-checked="true"] .tk-option__icon { color: #ffffff; }

.tk-option__text { flex: 1 1 auto; min-width: 0; }
.tk-option__title {
    margin: 0;
    font-size: var(--tk-text-sm);
    font-weight: 700;
    line-height: 1.25;
}
.tk-option__title--nowrap { white-space: nowrap; }
@media (min-width: 640px) { .tk-option__title--nowrap { white-space: normal; } }

.tk-option__desc {
    margin: 0.15rem 0 0;
    font-size: var(--tk-text-xs);
    line-height: 1.35;
    color: var(--tk-ink-muted);
}

.tk-option__check {
    position: absolute;
    top: var(--tk-space-3);
    right: var(--tk-space-3);
    line-height: 0;
    color: var(--tk-accent);
    opacity: 0;
    transition: opacity var(--tk-duration) var(--tk-ease);
}
.tk-option__check svg { width: 18px; height: 18px; }
.tk-option[aria-checked="true"] .tk-option__check { opacity: 1; }

/* A description sitting under a selected title should not fade out with the
   old blanket opacity: 0.8 -- it went muddy on the accent ground. */
.tk-option[aria-checked="true"] .tk-option__desc { color: var(--tk-ink-secondary); }

/* ---- Text and surface helpers ---------------------------------------- */
/* Named by role, not by shade. These replace the light/dark Tailwind PAIRS
   (text-gray-700 + dark:text-gray-200, and so on) that had to be kept in step
   by hand at every call site -- one token now carries both themes.
   Deliberately NOT a redefinition of Tailwind's own utilities: tokens.css
   explains why that approach was tried and abandoned. */
.tk-ink { color: var(--tk-ink); }
.tk-ink-soft { color: var(--tk-ink-secondary); }
.tk-ink-faint { color: var(--tk-ink-muted); }
.tk-surface-bg { background-color: var(--tk-surface); }
.tk-sunken-bg { background-color: var(--tk-surface-sunken); }
.tk-hairline { border-color: var(--tk-border); }

/* Accent and danger as roles, replacing the wizard's indigo and red literals. */
.tk-accent-text { color: var(--tk-accent); }
.tk-accent-bg { background-color: var(--tk-accent); color: #ffffff; }
.tk-danger-text { color: var(--tk-critical-ink); }

/* The photo route, first and full width: the route that gives the best
   results is the one the sheet opens on. */
/* Led by the accent, not by the action fill: on the dark theme that fill is
   near-white, and a white slab at the top of a dark sheet read as a different
   app's button. Tinted, outlined and with an accent disc, as the phone. */
.tk-photohero {
    display: flex; align-items: center; gap: var(--tk-space-3);
    width: 100%; margin-bottom: var(--tk-space-3);
    padding: var(--tk-space-4);
    border-radius: var(--tk-radius-lg, 14px);
    background-color: var(--tk-accent-soft); color: var(--tk-ink);
    border: 1px solid color-mix(in srgb, var(--tk-accent) 40%, transparent);
    text-align: left; font: inherit; cursor: pointer;
}
.tk-photohero::after {
    content: "\203A"; margin-left: auto;
    color: var(--tk-accent); font-size: 28px; line-height: 1; margin-top: -3px;
}
.tk-photohero:active { transform: scale(0.99); }
.tk-photohero__icon {
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px; flex: none; border-radius: 999px;
    background-color: color-mix(in srgb, var(--tk-accent) 18%, transparent);
    color: var(--tk-accent);
}
.tk-photohero__icon svg { width: 24px; height: 24px; }
.tk-photohero__text { display: flex; flex-direction: column; min-width: 0; }
.tk-photohero__title { font-weight: 800; font-size: var(--tk-text-base, 1rem); }
.tk-photohero__sub { font-size: var(--tk-text-xs, 0.75rem); color: var(--tk-ink-secondary); }
/* Out of the way while a result is being confirmed, like the routes. */
#logging-section.is-resulting:not(.is-picking) .tk-photohero { display: none; }

/* Recent foods: one line of chips above the categories, so the catalogue is
   not pushed a screen down. The phone's RecentStrip. */
.tk-recent { margin-bottom: var(--tk-space-3); }
.tk-recent__label {
    margin: 0 0 var(--tk-space-2);
    font-size: var(--tk-text-xs); font-weight: 700; letter-spacing: 0.06em;
    text-transform: uppercase; color: var(--tk-ink-muted);
}
.tk-recent__row { display: flex; gap: var(--tk-space-2); overflow-x: auto; scrollbar-width: none; padding-bottom: 2px; }
.tk-recent__row::-webkit-scrollbar { display: none; }
.tk-recent__chip {
    flex: none; display: inline-flex; align-items: center; gap: var(--tk-space-2);
    max-width: 14rem; min-height: 40px; padding: 0 var(--tk-space-3);
    border-radius: var(--tk-radius-full); border: 1px solid var(--tk-border);
    background: var(--tk-surface); color: var(--tk-ink);
    font: inherit; font-size: var(--tk-text-sm); font-weight: 700; cursor: pointer;
}
.tk-recent__chip span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tk-recent__chip small { color: var(--tk-ink-muted); font-weight: 500; font-variant-numeric: tabular-nums; }
.tk-recent__chip--copy { background: var(--tk-accent-soft); border-color: var(--tk-accent); color: var(--tk-accent); }
.tk-recent__chip--copy b { font-size: var(--tk-text-xs); }
/* The tray stands in for the browse zone's own heading while picking. */
#logging-section.is-resulting:not(.is-picking) .tk-recent { display: none; }

/* "Not right?" under a photo result: say what is wrong, the photo is re-read. */
.tk-photofix { display: flex; align-items: center; gap: var(--tk-space-2); margin: var(--tk-space-2) 0 var(--tk-space-3); }
.tk-photofix input {
    flex: 1; min-width: 0; min-height: 40px; padding: 0 var(--tk-space-3);
    border-radius: var(--tk-radius); border: 1px solid var(--tk-border);
    background: var(--tk-surface-sunken); color: var(--tk-ink); font: inherit; font-size: var(--tk-text-sm);
}
.tk-photofix__mic {
    flex: none; width: 40px; height: 40px; border-radius: var(--tk-radius-full);
    border: 1px solid var(--tk-border); background: none; color: var(--tk-ink-secondary);
    display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
}
.tk-photofix__mic svg { width: 20px; height: 20px; }
.tk-photofix__mic.is-live { border-color: var(--tk-critical); color: var(--tk-critical); }
.tk-photofix__go {
    flex: none; min-height: 40px; padding: 0 var(--tk-space-4); border: 0;
    border-radius: var(--tk-radius-full); background: var(--tk-action); color: var(--tk-action-ink);
    font: inherit; font-size: var(--tk-text-sm); font-weight: 800; cursor: pointer;
}
.tk-photofix__go:disabled { opacity: 0.6; }

/* Save -> a name, then Preset or Dish. */
.tk-traysave { display: flex; gap: var(--tk-space-2); align-items: center; margin-top: var(--tk-space-2); }
.tk-traysave input {
    flex: 1; min-width: 0; min-height: 40px; padding: 0 var(--tk-space-3);
    border-radius: var(--tk-radius); border: 1px solid var(--tk-border);
    background: var(--tk-surface-sunken); color: var(--tk-ink); font: inherit; font-size: var(--tk-text-sm);
}
.tk-traysave button { flex: none; min-height: 40px; padding: 0 var(--tk-space-3); font-size: var(--tk-text-sm); font-weight: 800; }
.tk-recent__chip--dish { border-color: var(--tk-accent); }

/* The dish window. */
.tk-dish { display: grid; gap: var(--tk-space-2); }
.tk-dish__total { font-size: var(--tk-text-xl, 1.5rem); font-weight: 800; color: var(--tk-ink); margin: 0; }
.tk-dish__total small { font-size: var(--tk-text-sm); font-weight: 600; color: var(--tk-ink-muted); }
.tk-dish__left { margin: 0; color: var(--tk-accent); font-weight: 800; font-size: var(--tk-text-sm); }
.tk-dish__ings summary { cursor: pointer; color: var(--tk-accent); font-weight: 700; font-size: var(--tk-text-sm); }
.tk-dish__ings div { display: flex; justify-content: space-between; gap: var(--tk-space-3); font-size: var(--tk-text-sm); color: var(--tk-ink-secondary); padding: 4px 0; }
.tk-dish__section {
    margin: var(--tk-space-3) 0 0; font-size: var(--tk-text-xs); font-weight: 700;
    letter-spacing: 0.06em; text-transform: uppercase; color: var(--tk-ink-muted);
}
.tk-dish__field { display: flex; align-items: center; gap: var(--tk-space-2); }
.tk-dish__field span { flex: 1; font-weight: 700; color: var(--tk-ink); font-size: var(--tk-text-sm); }
.tk-dish__field input {
    width: 7rem; min-height: 40px; padding: 0 var(--tk-space-3); text-align: right;
    border-radius: var(--tk-radius); border: 1px solid var(--tk-border);
    background: var(--tk-surface-sunken); color: var(--tk-ink); font: inherit; font-weight: 700;
}
.tk-dish__field input#dish-amount { flex: 1; width: auto; }
.tk-dish__field input#dish-label { width: 6rem; text-align: left; font-weight: 500; }
.tk-dish__field em { font-style: normal; color: var(--tk-ink-secondary); min-width: 1.5rem; }
.tk-dish__muted { margin: 0; color: var(--tk-ink-muted); font-size: var(--tk-text-sm); }
.tk-dish__empty-title { margin: 0; font-weight: 800; color: var(--tk-ink); }
.tk-dish__chips { display: flex; gap: var(--tk-space-2); flex-wrap: wrap; }
.tk-dish__chip {
    min-width: 3.5rem; min-height: 40px; padding: 0 var(--tk-space-3); border-radius: var(--tk-radius-full);
    border: 1px solid var(--tk-border); background: none; color: var(--tk-ink); font: inherit; font-weight: 800; cursor: pointer;
}
.tk-dish__chip.is-on { background: var(--tk-accent-soft); border-color: var(--tk-accent); }
.tk-dish__portion { margin: var(--tk-space-2) 0 0; font-weight: 800; color: var(--tk-ink); }
.tk-dish__primary {
    min-height: 48px; border: 0; border-radius: var(--tk-radius-full); background: var(--tk-action);
    color: var(--tk-action-ink); font: inherit; font-weight: 800; cursor: pointer; margin-top: var(--tk-space-2);
}
.tk-dish__primary:disabled { opacity: 0.5; cursor: default; }
.tk-dish__ghost {
    min-height: 40px; border: 1px solid var(--tk-border); border-radius: var(--tk-radius-full);
    background: none; color: var(--tk-ink-secondary); font: inherit; font-weight: 700; cursor: pointer;
}
.tk-dish__delete { border: 0; background: none; color: var(--tk-critical-ink); font: inherit; font-weight: 700; padding: var(--tk-space-2); cursor: pointer; }
.tk-dish__row {
    display: flex; align-items: center; gap: var(--tk-space-3); width: 100%; text-align: left;
    padding: var(--tk-space-3) 0; border: 0; border-bottom: 1px solid var(--tk-border); background: none; color: var(--tk-ink); font: inherit; cursor: pointer;
}
.tk-dish__row span { flex: 1; display: flex; flex-direction: column; }
.tk-dish__row small { color: var(--tk-ink-muted); }
.tk-dish__row i { font-style: normal; color: var(--tk-ink-muted); font-size: 1.25rem; }
.tk-dish__over { margin: var(--tk-space-2) 0 0; color: var(--tk-warning-ink, var(--tk-critical-ink)); font-size: var(--tk-text-xs); font-weight: 700; }
.tk-dish__ings div { align-items: center; }
.tk-dish__ings div span:first-child { flex: 1; min-width: 0; }
.tk-dish__ings > .tk-dish__ghost { width: 100%; margin: var(--tk-space-2) 0; }
.tk-dish__x { width: 36px; height: 36px; border: 0; background: none; color: var(--tk-ink-muted); font-size: 1.25rem; cursor: pointer; border-radius: var(--tk-radius-full); }
.tk-dish__x:hover { color: var(--tk-critical-ink); }
.tk-dish__field input#dish-name { width: 12rem; text-align: left; font-weight: 600; }
.tk-dish__stale { display: flex; align-items: center; gap: var(--tk-space-2); padding: var(--tk-space-3); border-radius: var(--tk-radius-md); background: var(--tk-surface-sunken); }
.tk-dish__stale p { flex: 1; margin: 0; font-weight: 800; color: var(--tk-ink); }
.tk-dish__stale .tk-dish__ghost { padding: 0 var(--tk-space-3); }
.tk-dishtarget { display: flex; align-items: center; gap: var(--tk-space-2); margin: 0 var(--tk-space-4) var(--tk-space-2); padding: var(--tk-space-2) var(--tk-space-3); border-radius: var(--tk-radius-md); background: var(--tk-accent-soft); color: var(--tk-ink); font-weight: 700; font-size: var(--tk-text-sm); }
.tk-dishtarget.hidden { display: none; }
.tk-dishtarget span { flex: 1; }
.tk-dishtarget button { border: 0; background: none; color: var(--tk-accent); font: inherit; font-weight: 800; min-height: 36px; cursor: pointer; }
/* The dish's portion switch: the filled pill the phone's Dish screen uses --
   the surface-on-sunken selection barely showed in the dark theme. */
.tk-dish .tk-segmented .tk-segment[aria-pressed="true"] { background-color: var(--tk-action); color: var(--tk-action-ink); box-shadow: none; }
.tk-dish .tk-segmented { border-radius: var(--tk-radius-full); }
.tk-dish .tk-segment { border-radius: var(--tk-radius-full); }
/* The category strip as the phone draws it: outline chips, the open one
   filled -- not a segmented control, which reads as a mode switch. */
#quick-catalogue-container .tk-segmented { background: transparent; padding: 0; gap: var(--tk-space-2); border-radius: 0; }
#quick-catalogue-container .catalogue-filter-btn.tk-segment {
    flex: 0 0 auto; border: 1px solid var(--tk-border); border-radius: var(--tk-radius-full);
    background-color: var(--tk-surface); color: var(--tk-ink-secondary); font-weight: 600; padding: 0.5rem 0.875rem;
}
#quick-catalogue-container .tk-segmented .catalogue-filter-btn.tk-segment[aria-pressed="true"] {
    background-color: var(--tk-action); border-color: var(--tk-action); color: var(--tk-action-ink); box-shadow: none;
}
.tk-browse__label { margin-top: var(--tk-space-3); }
/* The avatar's initial on a tinted disc, as the phone draws it -- the solid
   accent disc was the loudest thing in the header. */
#profile-avatar-btn { background-color: var(--tk-accent-soft); color: var(--tk-accent); box-shadow: none; }
#profile-avatar-btn:hover { background-color: var(--tk-accent-soft); filter: brightness(1.1); }
/* Trends: the phone's range chips and From/To rows. */
.tk-reportranges { display: flex; flex-wrap: wrap; gap: var(--tk-space-2); margin-bottom: var(--tk-space-4); }
.tk-rangechip {
    min-height: 44px; padding: 0 var(--tk-space-4); border-radius: var(--tk-radius-full);
    border: 1px solid var(--tk-border); background: var(--tk-surface); color: var(--tk-ink-secondary);
    font: inherit; font-size: var(--tk-text-sm); font-weight: 700; cursor: pointer;
}
.tk-rangechip[aria-pressed="true"] { background: var(--tk-action); border-color: var(--tk-action); color: var(--tk-action-ink); }
.tk-reportdates { display: grid; gap: var(--tk-space-2); margin-bottom: var(--tk-space-5); }
.tk-reportdates__row { display: flex; align-items: center; justify-content: space-between; gap: var(--tk-space-3); }
.tk-reportdates__row span { color: var(--tk-ink-secondary); font-weight: 700; font-size: var(--tk-text-sm); }
.tk-reportdates__row input { min-height: 44px; padding: 0 var(--tk-space-3); text-align: right; font-weight: 700; max-width: 12rem; }
/* Today, as the phone's pill; it replaces the PRO badge while away. */
.tk-dash__today { min-height: 32px; padding: 0 0.75rem; border: 0; border-radius: var(--tk-radius-full); background-color: var(--tk-accent-soft); color: var(--tk-accent); font-size: var(--tk-text-sm); font-weight: 800; }
.tk-dash__today:hover { color: var(--tk-accent); filter: brightness(1.1); }
.tk-dash__titlerow.is-away .tk-dash__pro { display: none; }
#today-btn.tk-dash__today { margin-left: var(--tk-space-2); min-height: 36px; }
/* Suggest a meal, as the phone lays it out. */
.tk-remaining { padding: var(--tk-space-4); margin-bottom: var(--tk-space-4); border: 1px solid var(--tk-border); border-radius: var(--tk-radius-lg); background: var(--tk-surface); }
.tk-remaining__label, .tk-fieldlabel { display: block; margin: 0 0 var(--tk-space-2); color: var(--tk-ink-muted); font-size: var(--tk-text-xs); font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; }
.tk-remaining__kcal { margin: 0; color: var(--tk-ink-muted); font-weight: 700; }
.tk-remaining__kcal b { color: var(--tk-ink); font-size: 2.25rem; font-weight: 800; margin-right: 0.25rem; }
.tk-remaining__macros { margin: var(--tk-space-1) 0 0; color: var(--tk-ink-secondary); font-size: var(--tk-text-sm); }
.tk-chiprow { display: flex; flex-wrap: wrap; gap: var(--tk-space-2); }
.tk-segmented--chips { background: transparent; padding: 0; gap: var(--tk-space-2); }
.tk-segmented--chips .tk-segment { flex: 0 1 auto; min-height: 44px; padding: 0 var(--tk-space-5); border: 1px solid var(--tk-border); border-radius: var(--tk-radius-full); background: var(--tk-surface); }
.tk-segmented.tk-segmented--chips .tk-segment[aria-pressed="true"] { background: var(--tk-action); border-color: var(--tk-action); color: var(--tk-action-ink); box-shadow: none; }
/* Track progress: the phone's latest figure and its trend line. */
.tk-metricsummary__big { margin: 0; color: var(--tk-ink); font-size: 2rem; font-weight: 800; line-height: 1.1; }
.tk-metricsummary__big small { font-size: var(--tk-text-base); color: var(--tk-ink-muted); font-weight: 700; }
.tk-metricsummary__meta { margin: var(--tk-space-1) 0 0; color: var(--tk-ink-muted); font-size: var(--tk-text-sm); }
/* The fasting bar, as the phone's FastingCard: a dot, the phase, the
   countdown, and a fill for the time left in the phase. */
.tk-fastbar { position: relative; display: flex; align-items: center; justify-content: space-between; overflow: hidden;
    margin: 0 0 var(--tk-space-4); padding: var(--tk-space-4); border: 1px solid var(--tk-border); border-radius: var(--tk-radius-lg); background: var(--tk-surface); }
.tk-fastbar.hidden { display: none; }
.tk-fastbar__fill { position: absolute; left: 0; top: 0; bottom: 0; width: 0; background: color-mix(in srgb, var(--tk-accent) 14%, transparent); transition: width 1s linear; }
.tk-fastbar[data-phase="eating"] .tk-fastbar__fill { background: color-mix(in srgb, var(--tk-good) 14%, transparent); }
.tk-fastbar__lead { position: relative; display: flex; align-items: center; gap: var(--tk-space-2); }
.tk-fastbar__dot { width: 8px; height: 8px; border-radius: 999px; background: var(--tk-accent); }
.tk-fastbar[data-phase="eating"] .tk-fastbar__dot { background: var(--tk-good); }
.tk-fastbar__key { color: var(--tk-ink-secondary); font-size: var(--tk-text-sm); font-weight: 600; }
.tk-fastbar__value { position: relative; color: var(--tk-ink); font-size: var(--tk-text-lg); font-weight: 800; font-variant-numeric: tabular-nums; }
.tk-loghint { margin: var(--tk-space-2) 0 0; text-align: center; color: var(--tk-ink-muted); font-size: var(--tk-text-sm); }
.tk-loghint.hidden { display: none; }
/* The entry's sheet, as the phone's EditFoodSheet. */
.tk-editsheet__name { margin: 0 0 var(--tk-space-4); color: var(--tk-ink); font-size: var(--tk-text-lg); font-weight: 800; }
.tk-editsheet__micros { margin-top: var(--tk-space-4); }
.tk-editsheet__actions { display: grid; gap: var(--tk-space-3); margin-top: var(--tk-space-5); }
.tk-editsheet__save { width: 100%; min-height: 52px; border-radius: var(--tk-radius-full); font-weight: 800; }
.tk-editsheet__remove { width: 100%; min-height: 48px; border-radius: var(--tk-radius-full); border: 1px solid var(--tk-critical-ink); background: none; color: var(--tk-critical-ink); font: inherit; font-weight: 800; cursor: pointer; }
.tk-editsheet__cancel { border: 0; background: none; color: var(--tk-ink-muted); font: inherit; font-weight: 700; padding: var(--tk-space-2); cursor: pointer; }
.tk-editsheet__amount { margin-bottom: var(--tk-space-4); }
.tk-editsheet__amount.hidden { display: none; }
.tk-editsheet__amount input[type="number"] { width: 7rem; min-height: 52px; text-align: right; font-size: var(--tk-text-lg); font-weight: 800; }
.tk-editsheet__figures { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--tk-space-2); }
.tk-editsheet__figures > div { display: flex; flex-direction: column; align-items: center; padding: var(--tk-space-2); border: 1px solid var(--tk-border); border-radius: var(--tk-radius-md); background: var(--tk-surface-sunken); }
.tk-editsheet__figures label { color: var(--tk-ink-secondary); font-weight: 700; }
.tk-editsheet__figures input { width: 100%; border: 0 !important; background: transparent !important; text-align: center; font-size: var(--tk-text-lg); font-weight: 800; padding: 2px 0 !important; }
/* The unit under each figure, and the multiplier's track in the accent, as the phone's sheet. */
.tk-editsheet__unit { color: var(--tk-ink-muted); font-size: 10px; line-height: 1.2; }
.tk-editsheet__range { width: 100%; margin-top: var(--tk-space-2); accent-color: var(--tk-accent); cursor: pointer; }

/* ---- The keyboard ------------------------------------------------------- */

/* html.tk-kb-open: a text field has focus and the keyboard is up (see
   watchKeyboard). A sheet then takes everything above the keyboard, square
   and without its handle, the way a native sheet goes to full height to be
   typed in. It stayed at its resting height before, so the keyboard left a
   strip of it under a band of the dimmed page. */
.tk-kb-open .tk-sheet.menu-visible {
    top: var(--tk-vvtop, 0px);
    bottom: var(--tk-kb, 0px);
    max-height: none;
    border-top: 0;
    border-radius: 0;
    box-shadow: none;
    padding-top: env(safe-area-inset-top, 0px);
}
.tk-kb-open .tk-sheet__grab { display: none; }
.tk-kb-open .tk-sheet__head { padding-top: var(--tk-space-3); }
/* The keyboard covers the home indicator. */
.tk-kb-open .tk-sheet__body { padding-bottom: var(--tk-space-4); }

/* Windows: on a phone they are sheets pinned to the bottom, so they ride up
   with the visible area and scroll inside it rather than run under the
   keyboard. */
@media (max-width: 639px) {
    .tk-kb-open .modal-backdrop {
        top: var(--tk-vvtop, 0px);
        bottom: var(--tk-kb, 0px);
    }
    .tk-kb-open .modal-backdrop .modal-box {
        max-height: calc(100% - env(safe-area-inset-top, 0px));
        overflow-y: auto;
        padding-bottom: 0;
    }
    .tk-kb-open .tk-modalgrab { display: none; }
}

/* Nothing pinned to the bottom of the page rides up on the keyboard. */
.tk-kb-open .tk-bottomnav,
.tk-kb-open .tk-connection { display: none; }

/* A field scrolled to stays clear of the edge with its label showing. */
.tk-sheet input, .tk-sheet textarea, .tk-sheet select,
.modal-box input, .modal-box textarea, .modal-box select {
    scroll-margin: 44px 0 20px;
}

/* ---- Manual entry, a view of the log sheet ---------------------------- */

.tk-sheet__back {
    display: none;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    flex: 0 0 auto;
    margin-left: calc(var(--tk-space-2) * -1);
    border: 0;
    border-radius: var(--tk-radius-full);
    background: none;
    color: var(--tk-ink);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}
.tk-sheet__back svg { width: 20px; height: 20px; }
.tk-sheet__title--manual { display: none; }
#log-sheet.is-manual .tk-sheet__back { display: flex; }
#log-sheet.is-manual .tk-sheet__title--manual { display: block; flex: 1 1 auto; }
#log-sheet.is-manual #log-sheet-title,
#log-sheet.is-manual .tk-sheet__head .info-tooltip-container,
#log-sheet.is-manual .tk-photohero,
#log-sheet.is-manual .tk-searchwrap,
#log-sheet.is-manual .tk-routes,
#log-sheet.is-manual #ai-result-container,
#log-sheet.is-manual #logging-browse,
#log-sheet.is-manual #manual-content h3 { display: none !important; }

/* Portions under the amount, and Log another among the sheet's actions. */
.tk-editsheet__portions { margin-top: var(--tk-space-3); }
.tk-editsheet__portions input[type="range"] { width: 100%; margin-top: var(--tk-space-2); accent-color: var(--tk-accent); }
.tk-editsheet__another { width: 100%; min-height: 48px; border-radius: var(--tk-radius-full); border: 1px solid var(--tk-border-strong); background: none; color: var(--tk-ink); font: inherit; font-weight: 800; cursor: pointer; }

/* Sheet titles centred on the screen, between whatever sits at the two ends
   (the hint or Back on the left, Close on the right), as the phone's stack
   headers now are. Out of flow, so the controls either side cannot push it
   off centre; the close button holds the row's height. */
.tk-sheet__head { position: relative; min-height: 34px; }
.tk-sheet__title {
    /* Pinned to the content box's bottom edge -- the head's top padding
       changes with the keyboard, its bottom padding does not -- and as tall
       as the close button, so the two share a centre line. */
    position: absolute; left: 50%; bottom: var(--tk-space-3); transform: translateX(-50%);
    height: 34px; line-height: 34px;
    max-width: calc(100% - 128px);
    text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tk-sheet__close { margin-left: auto; }
.tk-sheet--log .tk-sheet__head .info-tooltip-container { margin-left: 0; }

/* ---- Recent, a view of the log sheet ------------------------------------ */
.tk-recent__chip--all { border-color: var(--tk-accent); color: var(--tk-accent); font-weight: 800; }
.tk-sheet__title--recent { display: none; }
#log-sheet.is-recent .tk-sheet__back { display: flex; }
#log-sheet.is-recent .tk-sheet__title--recent { display: block; }
#log-sheet.is-recent #log-sheet-title,
#log-sheet.is-recent .tk-sheet__head .info-tooltip-container,
#log-sheet.is-recent .tk-photohero,
#log-sheet.is-recent .tk-searchwrap,
#log-sheet.is-recent .tk-routes,
#log-sheet.is-recent #manual-content,
#log-sheet.is-recent #ai-result-container,
#log-sheet.is-recent #logging-browse { display: none !important; }
.tk-recentall__filter {
    width: 100%; min-height: 48px; margin-bottom: var(--tk-space-3);
    padding: 0 var(--tk-space-4); border-radius: var(--tk-radius-lg);
    border: 1px solid var(--tk-border-strong); background: var(--tk-surface); color: var(--tk-ink); font: inherit;
}
.tk-recentall__list { border: 1px solid var(--tk-border); border-radius: var(--tk-radius-lg); overflow: hidden; background: var(--tk-surface); }
.tk-recentall__row {
    display: flex; align-items: center; gap: var(--tk-space-3); width: 100%; min-height: 56px;
    padding: var(--tk-space-3) var(--tk-space-4); border: 0; border-bottom: 1px solid var(--tk-border);
    background: none; color: var(--tk-ink); font: inherit; text-align: left; cursor: pointer;
}
.tk-recentall__row:last-child { border-bottom: 0; }
.tk-recentall__row:active { background: var(--tk-surface-sunken); }
.tk-recentall__text { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.tk-recentall__name { font-weight: 700; }
.tk-recentall__text small { color: var(--tk-ink-muted); font-size: var(--tk-text-xs); margin-top: 2px; }
.tk-recentall__kcal { color: var(--tk-ink-secondary); font-weight: 700; font-size: var(--tk-text-sm); font-variant-numeric: tabular-nums; }
.tk-recentall__empty { color: var(--tk-ink-muted); text-align: center; padding: var(--tk-space-6) var(--tk-space-4); margin: 0; }

/* ---- Settings, as the phone lays it out --------------------------------- */
.tk-settings__head { position: relative; display: flex; justify-content: flex-end; align-items: center; min-height: 34px; padding: var(--tk-space-2) var(--tk-space-4) var(--tk-space-3); }
.tk-settings__title { position: absolute; left: 50%; transform: translateX(-50%); margin: 0; font-size: var(--tk-text-lg); font-weight: 800; color: var(--tk-ink); }
.tk-settings { display: flex; flex-direction: column; gap: var(--tk-space-3); padding: 0 var(--tk-space-4) var(--tk-space-5); }
.tk-settings section { display: flex; flex-direction: column; }
.tk-settings__card { padding: var(--tk-space-4); border: 1px solid var(--tk-border); border-radius: var(--tk-radius-lg); background: var(--tk-surface); }
.tk-settings__section { margin: var(--tk-space-2) 0 var(--tk-space-2); color: var(--tk-ink-muted); font-size: var(--tk-text-xs); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; }
.tk-settings__whohead { display: flex; gap: var(--tk-space-4); align-items: center; }
.tk-settings__avatar { width: 64px; height: 64px; flex: none; border-radius: 999px; object-fit: cover; border: 1px solid var(--tk-border); }
.tk-settings__whotext { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.tk-settings__label { margin: 0; color: var(--tk-ink-muted); font-size: var(--tk-text-xs); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; }
.tk-settings__label--spaced { margin-top: var(--tk-space-4); }
.tk-settings__email { color: var(--tk-ink); font-weight: 700; overflow-wrap: anywhere; }
.tk-settings__value { color: var(--tk-ink); font-weight: 700; margin-top: 2px; }
.tk-settings__value .text-sm { font-weight: 400; color: var(--tk-ink-muted); }
.tk-settings__tag { color: var(--tk-ink-muted); font-size: var(--tk-text-sm); }
.tk-settings__row { display: flex; align-items: center; justify-content: space-between; gap: var(--tk-space-3); }
.tk-settings__link { border: 0; background: none; padding: 0; color: var(--tk-accent); font: inherit; font-weight: 700; cursor: pointer; text-decoration: none; }
.tk-settings .tk-settings__stats { display: grid; grid-template-columns: 1fr 1fr; text-align: center; }
.tk-settings__stats b { display: block; color: var(--tk-ink); font-size: var(--tk-text-xl); font-weight: 800; }
.tk-settings__stats span { color: var(--tk-ink-muted); font-size: var(--tk-text-xs); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; }
.tk-settings__secondary { display: flex; width: 100%; min-height: 48px; align-items: center; justify-content: center; gap: var(--tk-space-2); border: 1px solid var(--tk-border-strong); border-radius: var(--tk-radius-full); background: none; color: var(--tk-accent); font: inherit; font-weight: 700; cursor: pointer; text-decoration: none; }
.tk-settings__primary { width: 100%; min-height: 52px; margin-top: var(--tk-space-3); border: 0; border-radius: var(--tk-radius-full); background: var(--tk-action); color: var(--tk-action-ink); font: inherit; font-weight: 800; cursor: pointer; }
.tk-settings__danger { width: 100%; min-height: 48px; margin-top: var(--tk-space-3); border: 1px solid var(--tk-critical-ink); border-radius: var(--tk-radius-full); background: none; color: var(--tk-critical-ink); font: inherit; font-weight: 700; cursor: pointer; }
.tk-settings__note { margin: 0 0 var(--tk-space-3); color: var(--tk-ink-muted); font-size: var(--tk-text-sm); line-height: 1.45; }
.tk-settings__body { margin: 0; color: var(--tk-ink); line-height: 1.45; }
.tk-settings__legal { display: flex; justify-content: center; gap: var(--tk-space-3); margin: 0; color: var(--tk-ink-muted); }
.tk-settings__legal a { color: var(--tk-accent); font-weight: 700; text-decoration: none; }
.tk-settings__theme { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--tk-space-2); }
.tk-settings__theme .tk-segment.is-active { background: var(--tk-accent); color: var(--tk-surface); }
.tk-settings__langs { display: flex; flex-wrap: wrap; gap: var(--tk-space-2); }
.tk-settings__lang { min-height: 40px; padding: 0 var(--tk-space-4); border: 1px solid var(--tk-border); border-radius: var(--tk-radius-full); background: var(--tk-surface-sunken); color: var(--tk-ink); font: inherit; font-weight: 700; cursor: pointer; }
.tk-settings__lang.is-active { background: var(--tk-accent); border-color: var(--tk-accent); color: var(--tk-surface); }
.tk-settings__uid { margin: var(--tk-space-2) 0 0; text-align: center; color: var(--tk-ink-muted); font-size: var(--tk-text-xs); user-select: all; }
.tk-settings__uidvalue { font-family: ui-monospace, monospace; }
#settings-password-content { margin-top: var(--tk-space-4); }

/* ---- Set your goals, opened from More: the phone's screen --------------- */
#ai-goals-modal.is-update #goals-tabs { display: none; }
#ai-goals-modal:not(.is-update) #goals-work-out,
#ai-goals-modal:not(.is-update) #goals-back-to-numbers { display: none; }
.tk-goals__opener {
    display: flex; flex-direction: column; align-items: flex-start; gap: 2px; width: 100%;
    padding: var(--tk-space-4); border: 1px solid var(--tk-accent); border-radius: var(--tk-radius-lg);
    background: var(--tk-accent-soft); font: inherit; text-align: left; cursor: pointer;
}
.tk-goals__openertitle { color: var(--tk-accent); font-weight: 800; font-size: var(--tk-text-base); }
.tk-goals__openerhint { color: var(--tk-ink-secondary); font-size: var(--tk-text-sm); }
.tk-goals__back { border: 0; background: none; padding: 0 0 var(--tk-space-3); color: var(--tk-accent); font: inherit; font-weight: 700; cursor: pointer; }
.tk-goals__back::before { content: "\2039  "; }
.tk-goals__kcal { display: flex; flex-direction: column; gap: 2px; padding: var(--tk-space-4); border: 1px solid var(--tk-border); border-radius: var(--tk-radius-lg); background: var(--tk-surface); }
.tk-goals__kcallabel { color: var(--tk-ink-muted); font-size: var(--tk-text-xs); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; }
.tk-goals__kcalvalue { color: var(--tk-ink-muted); font-weight: 700; }
.tk-goals__kcalvalue b { color: var(--tk-ink); font-size: 2.25rem; font-weight: 800; letter-spacing: -0.02em; margin-right: 2px; }
#ai-goals-modal:not(.is-update) .tk-goals__kcal { display: none; }

/* A long username wraps inside the Profile card instead of running out of it. */
#profile-username { min-width: 0; overflow-wrap: anywhere; text-align: center; }
div:has(> #profile-username) { max-width: 100%; justify-content: center; }

/* The barcode typed, when the camera cannot read it -- the phone's row. */
.tk-typedbarcode { display: flex; gap: var(--tk-space-2); margin-top: var(--tk-space-3); }
.tk-typedbarcode.hidden { display: none; }
.tk-typedbarcode input { flex: 1; min-width: 0; min-height: 48px; padding: 0 var(--tk-space-3); font-size: var(--tk-text-base); letter-spacing: 0.04em; }
.tk-typedbarcode button { min-height: 48px; padding: 0 var(--tk-space-4); border-radius: var(--tk-radius-md); font-weight: 800; }
.tk-typedbarcode button:disabled { opacity: 0.5; }

/* Window titles centred, the close button pinned to the end, as the sheet
   titles and the phone's headers are. Only headers that are a title and a
   close button -- not the ones with a Back button or tabs in them. The header
   stays sticky; a sticky box is a containing block for the button. */
.modal-header.justify-between:has(> button[id^="close"], > #dish-close):not(:has(#back-to-suggestions)) {
    justify-content: center; text-align: center; padding-left: 56px; padding-right: 56px;
}
.modal-header.justify-between:has(> button[id^="close"], > #dish-close):not(:has(#back-to-suggestions)) > :is(button[id^="close"], #dish-close) {
    position: absolute; right: var(--tk-space-4); top: 50%; transform: translateY(-50%);
}
/* A header that is not sticky is not positioned, and the button went to the
   window instead -- Track progress had its close beside the Date box. */
.modal-header.justify-between:not(.sticky):has(> button[id^="close"], > #dish-close) { position: relative; }
/* The three ranges on one row, as the phone fits them. */
.tk-reportranges { flex-wrap: nowrap; }
.tk-reportranges .tk-rangechip { flex: 1 1 0; min-width: 0; padding: 4px var(--tk-space-2); line-height: 1.2; text-align: center; }

/* Manual entry's micronutrients, folded as the phone folds them. */
.tk-manualmicros > summary { cursor: pointer; color: var(--tk-accent); font-weight: 700; list-style: none; display: flex; justify-content: space-between; align-items: center; }
.tk-manualmicros > summary::-webkit-details-marker { display: none; }
.tk-manualmicros > summary::after { content: "\2304"; color: var(--tk-accent); }
.tk-manualmicros[open] > summary::after { content: "\2303"; }

.tk-settings__photolinks { display: flex; gap: var(--tk-space-4); align-items: center; }
.tk-settings__link--muted { color: var(--tk-ink-muted); font-weight: 600; }
.tk-settings__link.hidden { display: none; }

/* The target after the calories eaten, muted as the phone's hero has it. */
.tk-hero__of { color: var(--tk-ink-muted); }

/* Save on the entry sheet: a pill, muted until something is changed, as the phone's. */
.btn-primary.tk-editsheet__save { border-radius: var(--tk-radius-full); }
.btn-primary.tk-editsheet__save:disabled { background: var(--tk-ink-muted); border-color: var(--tk-ink-muted); color: var(--tk-action-ink); opacity: 1; cursor: default; }

/* ---- Set your goals: the phone's rows ----------------------------------- */
.tk-goalcard { background: var(--tk-surface); border: 1px solid var(--tk-border); border-radius: var(--tk-radius-lg); padding: var(--tk-space-2) var(--tk-space-4); }
.tk-goalrow { display: flex; align-items: center; justify-content: space-between; gap: var(--tk-space-3); min-height: 60px; cursor: text; }
.tk-goalrow__name { display: flex; align-items: center; flex-wrap: wrap; gap: 0 var(--tk-space-2); min-width: 0; color: var(--tk-ink); font-weight: 700; }
.tk-goalrow__dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.tk-goalrow__hint { color: var(--tk-ink-muted); font-weight: 500; font-size: var(--tk-text-sm); }
.tk-goalrow__box { display: flex; align-items: center; gap: var(--tk-space-2); flex: none; }
#ai-goals-modal .tk-goalrow__box input { width: 6rem; text-align: right; font-weight: 800; font-size: var(--tk-text-lg); padding: var(--tk-space-2) var(--tk-space-3); border-radius: var(--tk-radius-md); background: var(--tk-surface-sunken); border: 1px solid var(--tk-border); color: var(--tk-ink); }
.tk-goalrow__unit { width: 1.75rem; color: var(--tk-ink-muted); font-weight: 700; }
.tk-goalcard__note { margin: var(--tk-space-2) 0; color: var(--tk-ink-muted); font-size: var(--tk-text-sm); line-height: 1.4; }

/* The Presets and Saved recipes panes in More: the app's sunken surface (an
   old .dark .accordion-content painted them slate), and New preset in the
   accent, as the phone's Presets screen has it. */
.tk-tools > .tab-pane.accordion-content { background-color: var(--tk-surface-sunken); }
#new-preset-btn { color: var(--tk-accent); }

/* ---- Invite friends ----------------------------------------------------- */
.tk-invite__badge { background: var(--tk-accent-soft); color: var(--tk-accent); }
.tk-invite { display: flex; flex-direction: column; gap: var(--tk-space-3); }
.tk-invite .modal-header { margin: 0; padding: 0; background: transparent; border: 0; position: relative; }
.tk-invite__hero { background: var(--tk-accent-soft); border-radius: var(--tk-radius-lg); padding: var(--tk-space-4); text-align: center; }
.tk-invite__title { font-size: var(--tk-text-lg); font-weight: 800; color: var(--tk-ink); }
.tk-invite__sub { margin-top: var(--tk-space-1); color: var(--tk-ink-muted); font-size: var(--tk-text-sm); line-height: 1.4; }
.tk-invite__link { display: flex; gap: var(--tk-space-2); align-items: stretch; }
.tk-invite__link code, .tk-invite__link input { flex: 1; min-width: 0; padding: var(--tk-space-3); border-radius: var(--tk-radius-md); background: var(--tk-surface-sunken); border: 1px solid var(--tk-border); color: var(--tk-ink); font-size: var(--tk-text-sm); white-space: nowrap; }
.tk-invite__link code { font-family: inherit; font-weight: 700; color: var(--tk-accent); overflow: hidden; text-overflow: ellipsis; }
.tk-invite__link input { text-transform: uppercase; font-weight: 700; letter-spacing: .04em; }
.tk-invite__btn { flex: none; padding: 0 var(--tk-space-4); border-radius: var(--tk-radius-md); background: var(--tk-surface); border: 1px solid var(--tk-border-strong, var(--tk-border)); color: var(--tk-ink); font-weight: 700; }
.tk-invite__btn:disabled { opacity: .5; }
.btn-primary.tk-invite__share { width: 100%; border-radius: 999px; padding: var(--tk-space-3); font-weight: 800; }
.tk-invite__stats { display: grid; grid-template-columns: 1fr 1fr; gap: var(--tk-space-2); }
.tk-invite__stats div { background: var(--tk-surface); border: 1px solid var(--tk-border); border-radius: var(--tk-radius-md); padding: var(--tk-space-3); text-align: center; display: flex; flex-direction: column; gap: 2px; }
.tk-invite__stats b { font-size: var(--tk-text-xl, 1.25rem); color: var(--tk-ink); font-variant-numeric: tabular-nums; }
.tk-invite__stats span { font-size: var(--tk-text-xs, .75rem); color: var(--tk-ink-muted); font-weight: 600; }
.tk-invite__note { font-size: var(--tk-text-sm); color: var(--tk-ink-muted); }
.tk-invite__note--bad { color: var(--tk-critical-ink); }
.tk-invite__redeem { display: flex; flex-direction: column; gap: var(--tk-space-2); border-top: 1px solid var(--tk-border); padding-top: var(--tk-space-3); }
.tk-invite__fine { font-size: var(--tk-text-xs, .75rem); color: var(--tk-ink-muted); line-height: 1.4; }
.tk-invite__fine a { color: var(--tk-accent); font-weight: 700; text-decoration: underline; }

/* ---- Working out a typed meal ------------------------------------------- */
/* Three bars in the macro colours, rising and falling out of step, beside the
   step it is on and a Cancel. Under reduced motion they hold still and the
   card breathes instead, slowly. */
.tk-thinking { display: flex; align-items: center; gap: var(--tk-space-3); margin-top: var(--tk-space-3); padding: var(--tk-space-3) var(--tk-space-4); border-radius: var(--tk-radius-lg); background: var(--tk-surface); border: 1px solid var(--tk-border); position: relative; overflow: hidden; }
.tk-thinking::after { content: ''; position: absolute; inset: 0; background: linear-gradient(100deg, transparent 20%, color-mix(in srgb, var(--tk-accent) 12%, transparent) 50%, transparent 80%); transform: translateX(-100%); animation: tk-thinking-sweep 1.8s ease-in-out infinite; pointer-events: none; }
.tk-thinking__bars { display: flex; align-items: flex-end; gap: 4px; height: 28px; flex: none; }
.tk-thinking__bars i { width: 6px; height: 100%; border-radius: 3px; transform-origin: bottom; animation: tk-thinking-bar 1.1s ease-in-out infinite; }
.tk-thinking__bars i:nth-child(1) { background: var(--tk-protein); }
.tk-thinking__bars i:nth-child(2) { background: var(--tk-carbs); animation-delay: .18s; }
.tk-thinking__bars i:nth-child(3) { background: var(--tk-fat); animation-delay: .36s; }
.tk-thinking__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.tk-thinking__step { margin: 0; font-size: var(--tk-text-sm); font-weight: 700; color: var(--tk-ink); }
.tk-thinking__lines { display: flex; gap: 6px; }
.tk-thinking__lines span { height: 8px; border-radius: 4px; background: var(--tk-surface-sunken); animation: tk-thinking-pulse 1.4s ease-in-out infinite; }
.tk-thinking__lines span:first-child { width: 55%; }
.tk-thinking__lines span:last-child { width: 25%; animation-delay: .2s; }
.tk-thinking__cancel { flex: none; position: relative; z-index: 1; padding: 6px 14px; border-radius: 999px; border: 1px solid var(--tk-border-strong); background: var(--tk-surface); color: var(--tk-ink); font-weight: 700; font-size: var(--tk-text-sm); }
.tk-thinking__cancel:hover { background: var(--tk-surface-sunken); }
@keyframes tk-thinking-bar { 0%, 100% { transform: scaleY(.3); } 50% { transform: scaleY(1); } }
@keyframes tk-thinking-pulse { 0%, 100% { opacity: .45; } 50% { opacity: 1; } }
@keyframes tk-thinking-sweep { to { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) {
    .tk-thinking::after, .tk-thinking__bars i, .tk-thinking__lines span { animation: none; }
    .tk-thinking__bars i { transform: scaleY(.7); }
    .tk-thinking { animation: tk-thinking-pulse 2.4s ease-in-out infinite; }
}
.tk-thinking__bars--inline { height: 14px; gap: 3px; margin-right: 8px; display: inline-flex; vertical-align: middle; }
.tk-thinking__bars--inline i { width: 3px; }
.btn-primary .tk-thinking__bars--inline i { background: currentColor !important; }

/* ====================================================================== */
/* Signed-out home page                                                    */
/* ====================================================================== */

/* Shown only once the head script has decided nobody is signed in: hidden
   while it checks (tk-wait) and for anyone signed in (tk-returning). The
   boot spinner is the other way round -- it is for a day that is loading,
   and a new visitor has no day to load. */
.tk-landing { display: none; }
html:not(.tk-wait):not(.tk-returning) .tk-landing { display: block; }
html:not(.tk-wait):not(.tk-returning) #loading { display: none; }

.tk-landing__hero {
    padding: var(--tk-space-4) 0 var(--tk-space-6);
}
html:not(.tk-wait):not(.tk-returning) .tk-landing__hero {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--tk-space-4);
    align-items: center;
}
@media (min-width: 640px) {
    html:not(.tk-wait):not(.tk-returning) .tk-landing__hero { grid-template-columns: 1.25fr 1fr; }
}

.tk-landing__kicker {
    display: inline-block;
    font-size: var(--tk-text-xs);
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--tk-accent);
    background-color: var(--tk-accent-soft);
    padding: 0.35rem 0.75rem;
    border-radius: var(--tk-radius-full);
    margin-bottom: var(--tk-space-3);
}
.tk-landing__title {
    font-size: clamp(2rem, 8vw, var(--tk-text-hero));
    line-height: 1.05;
    font-weight: 700;
    letter-spacing: -0.03em;
    color: var(--tk-ink);
}
.tk-landing__title span { display: block; }
.tk-landing__grad {
    background: var(--tk-brand-gradient);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}
.tk-landing__lede {
    margin-top: var(--tk-space-3);
    font-size: var(--tk-text-lg);
    line-height: 1.5;
    color: var(--tk-ink-secondary);
}
.tk-landing__ctas {
    display: flex;
    flex-wrap: wrap;
    gap: var(--tk-space-2);
    margin-top: var(--tk-space-4);
}
.tk-landing__cta { font-size: var(--tk-text-base); padding-left: 1.5rem; padding-right: 1.5rem; }
.tk-landing__note {
    margin-top: var(--tk-space-3);
    font-size: var(--tk-text-sm);
    color: var(--tk-ink-muted);
}
.tk-landing__shot {
    display: block;
    width: 100%;
    max-width: 300px;
    height: auto;
    margin: 0 auto;
}

.tk-landing__more { padding: var(--tk-space-6) 0 var(--tk-space-5); }
.tk-landing__h2 {
    font-size: var(--tk-text-xl);
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--tk-ink);
    margin-bottom: var(--tk-space-3);
}
.tk-landing__features {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--tk-space-3);
    list-style: none;
    padding: 0;
    margin: 0 0 var(--tk-space-5);
}
@media (min-width: 640px) {
    .tk-landing__features { grid-template-columns: 1fr 1fr; }
}
.tk-landing__feature,
.tk-landing__panel {
    background-color: var(--tk-surface);
    border: 1px solid var(--tk-border);
    border-radius: var(--tk-radius-lg);
    padding: var(--tk-space-4);
}
.tk-landing__panel { margin-bottom: var(--tk-space-3); }
.tk-landing__panel p,
.tk-landing__feature p {
    font-size: var(--tk-text-sm);
    line-height: 1.5;
    color: var(--tk-ink-secondary);
}
.tk-landing__feature h3 {
    font-size: var(--tk-text-base);
    font-weight: 700;
    color: var(--tk-ink);
    margin: var(--tk-space-2) 0 var(--tk-space-1);
}
.tk-landing__icon { font-size: 1.5rem; line-height: 1; }
.tk-landing__footer {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--tk-space-2) var(--tk-space-4);
    margin-top: var(--tk-space-5);
    font-size: var(--tk-text-sm);
}
.tk-landing__footer a {
    color: var(--tk-ink-muted);
    text-decoration: underline;
    text-underline-offset: 3px;
    padding: var(--tk-space-1) 0;
}
.tk-landing__footer a:hover { color: var(--tk-ink); }
