/* ------------------------------------------------------------ Habits slide ---
   Epic onfiremobile-njbiw. Design: docs/PRD/displays/habits/DESIGN-v2.md
   (§1 columns + card, §2 weekly progress, §4.2 celebration, §4.3 detail
   overlay, §5 animated background), with DESIGN.md §6 still supplying the dot
   palette.

   Loaded after styles.css; every class here is .ns-hb*. The shell reuses
   .ns-root / .ns-head / .ns-title / .ns-count / .ns-more from styles.css and
   adds nothing to them. Sizes are clamp()/min()/max() + vmin, never a bare
   length: the same markup has to read from across a room on a 1280x720
   Raspberry Pi panel and on a 4K wall. The only exception is the ONE media
   query that collapses a portrait/narrow panel to a single column, which is
   by definition about a real device width.

   Palette tokens are declared on the root so the scifi_neon skin can reskin
   the whole board by redefining a handful of custom properties instead of
   restating every rule. */

.ns-hb {
    --hb-done: #00A662;
    --hb-empty: #164A4A;
    --hb-letter: #809597;
    --hb-name: #fff;
    --hb-ink: #002b2e;
    --hb-blob-a: #00A662;
    --hb-blob-b: #164A4A;
    --hb-blob-c: #0a6b7a;
    --hb-track: #258477;
    --hb-fill: #00ff19;
    --hb-err: #FF5A4D;
    --hb-card: rgba(0, 0, 0, 0.3);
    --hb-card-edge: rgba(255, 255, 255, 0.3);

    /* The habits page is its own flat ink, not the shared ns-root gradient —
       the animated blobs below are what gives it depth. position+isolation
       create the stacking context the z-index:-1 background sits inside, so
       it paints above this colour and below every piece of content. */
    background: var(--hb-ink);
    position: relative;
    isolation: isolate;
}

/* --- §5 animated background ------------------------------------------------
   Pure CSS. Three large radial-gradient blobs, blurred once by a STATIC
   filter and then moved with transform only. Animating `filter` or
   `background-position` would repaint every frame, for days, on a Pi; a
   transform on a composited layer costs the GPU almost nothing.
   mountSlideShader is deliberately not used: it is a WebGL canvas and this
   has to run alongside whatever else the display is doing, indefinitely. */
.ns-hb-bg {
    position: absolute;
    inset: 0;
    z-index: -1;
    overflow: hidden;
    pointer-events: none;
}

.ns-hb-blob {
    position: absolute;
    display: block;
    width: 70%;
    height: 70%;
    border-radius: 50%;
    filter: blur(clamp(40px, 8vmin, 130px));
    opacity: 0.55;
    will-change: transform;
    transform: translate3d(0, 0, 0);
}

.ns-hb-blob-a {
    left: -12%;
    top: -18%;
    background: radial-gradient(circle at 50% 50%, var(--hb-blob-a) 0%, rgba(0, 166, 98, 0) 70%);
    animation: ns-hb-drift-a 46s ease-in-out infinite alternate;
}
.ns-hb-blob-b {
    right: -16%;
    top: 12%;
    background: radial-gradient(circle at 50% 50%, var(--hb-blob-c) 0%, rgba(10, 107, 122, 0) 70%);
    animation: ns-hb-drift-b 58s ease-in-out infinite alternate;
    opacity: 0.45;
}
.ns-hb-blob-c {
    left: 22%;
    bottom: -26%;
    background: radial-gradient(circle at 50% 50%, var(--hb-blob-b) 0%, rgba(22, 74, 74, 0) 70%);
    animation: ns-hb-drift-c 52s ease-in-out infinite alternate;
    opacity: 0.6;
}

@keyframes ns-hb-drift-a {
    from { transform: translate3d(0, 0, 0) scale(1); }
    to   { transform: translate3d(14%, 9%, 0) scale(1.12); }
}
@keyframes ns-hb-drift-b {
    from { transform: translate3d(0, 0, 0) scale(1.08); }
    to   { transform: translate3d(-12%, 14%, 0) scale(0.94); }
}
@keyframes ns-hb-drift-c {
    from { transform: translate3d(0, 0, 0) scale(0.96); }
    to   { transform: translate3d(10%, -12%, 0) scale(1.14); }
}

/* --- §2 weekly progress ---------------------------------------------------- */
.ns-hb-progress {
    display: flex;
    align-items: baseline;
    gap: clamp(8px, 1.6vmin, 24px);
    white-space: nowrap;
}
.ns-hb-stages {
    color: var(--hb-name);
    font-size: clamp(16px, 2.6vmin, 36px);
    font-weight: 700;
    letter-spacing: -0.01em;
}
.ns-hb-percent {
    color: var(--hb-done);
    font-size: clamp(16px, 2.6vmin, 36px);
    font-weight: 700;
}

.ns-hb-bar {
    flex: 0 0 auto;
    height: clamp(3px, 0.4vmin, 7px);
    border-radius: 999em;
    background: var(--hb-track);
    overflow: hidden;
}
.ns-hb-bar-fill {
    display: block;
    height: 100%;
    width: 0;
    border-radius: 999em;
    background: var(--hb-fill);
    box-shadow: 0 clamp(2px, 0.4vmin, 6px) clamp(2px, 0.4vmin, 6px) rgba(0, 0, 0, 0.25);
    /* Same easeOutCubic as the dot reveal; it animates once, on data change. */
    transition: width 900ms cubic-bezier(0.215, 0.61, 0.355, 1);
}

/* --- §1 three columns ------------------------------------------------------ */
.ns-hb-grid {
    flex: 1 1 auto;
    min-height: 0;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: clamp(16px, 3vmin, 48px);
}

/* A time-of-day period with no habits is not rendered at all (owner decision,
   2026-09-20), so three tracks would leave a phantom empty one taking a third
   of the wall. The renderer writes how many sections it actually drew into
   `data-cols`, and the columns present share the width. A count, not :has(),
   which is not on every display browser. */
.ns-hb-grid[data-cols="1"] { grid-template-columns: 1fr; }
.ns-hb-grid[data-cols="2"] { grid-template-columns: repeat(2, 1fr); }

.ns-hb-col {
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 0;
    gap: clamp(8px, 1.4vmin, 22px);
    /* The two columns that are not the display's current time of day. */
    opacity: 0.5;
    /* `gap` rides along so a period folding shut in portrait does not leave a
       stranded strip of dead space between its header and nothing. */
    transition: opacity 700ms ease,
                gap 340ms cubic-bezier(0.215, 0.61, 0.355, 1);
}
.ns-hb-col-active { opacity: 1; }

/* The period header. A <button> on an interactive column board (it folds the
   period open or shut in portrait), an inert <div> otherwise, so the reset
   declarations below have to be harmless for both. */
.ns-hb-col-head {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: clamp(6px, 1.2vmin, 18px);
    min-width: 0;
    width: 100%;
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    -webkit-tap-highlight-color: transparent;
}
button.ns-hb-col-head:focus-visible {
    outline: max(2px, 0.2vmin) solid var(--hb-done);
    outline-offset: clamp(2px, 0.3vmin, 5px);
    border-radius: clamp(6px, 1vmin, 14px);
}
.ns-hb-col-title {
    min-width: 0;
    color: rgba(255, 255, 255, 0.72);
    font-size: clamp(13px, 2.1vmin, 30px);
    font-weight: 300;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The at-a-glance count and the fold affordance. Both are PORTRAIT furniture:
   a landscape board never collapses, so showing a chevron there would offer a
   control that does nothing. The portrait media query switches them on. */
.ns-hb-col-sum {
    display: none;
    flex: 0 0 auto;
    margin-left: auto;
    color: var(--hb-done);
    font-size: clamp(11px, 1.8vmin, 26px);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.02em;
    white-space: nowrap;
}
.ns-hb-chev {
    display: none;
    flex: 0 0 auto;
    width: clamp(12px, 2vmin, 28px);
    height: clamp(12px, 2vmin, 28px);
    fill: none;
    stroke: rgba(255, 255, 255, 0.55);
    stroke-width: 2.2;
    stroke-linecap: round;
    stroke-linejoin: round;
    /* Drawn pointing down; open is the rotated state, so there is one glyph
       and one transform transition rather than two glyphs to keep in step. */
    transform: rotate(180deg);
    transition: transform 340ms cubic-bezier(0.215, 0.61, 0.355, 1);
}
.ns-hb-col-shut .ns-hb-chev { transform: rotate(0deg); }

/* The period body: everything the header folds away. A one-row grid, because
   animating its track from 1fr to 0fr is a TRANSITION on a user-initiated
   change -- not a keyframe loop -- so it costs a Raspberry Pi nothing while
   nobody is touching the display. Inert in landscape: the rule that sets 0fr
   lives inside the portrait media query, and only there. */
.ns-hb-col-body {
    flex: 1 1 auto;
    min-height: 0;
    display: grid;
    grid-template-rows: 1fr;
    overflow: hidden;
    transition: grid-template-rows 340ms cubic-bezier(0.215, 0.61, 0.355, 1),
                opacity 240ms ease;
}
.ns-hb-slot-icon {
    flex: 0 0 auto;
    width: clamp(15px, 2.4vmin, 34px);
    height: clamp(15px, 2.4vmin, 34px);
    fill: none;
    stroke: var(--hb-done);
    stroke-width: 1.6;
    stroke-linecap: round;
}
.ns-hb-slot-icon .ns-hb-slot-fill { fill: var(--hb-done); stroke: none; }

/* The card. Only the ACTIVE column carries the chrome (DESIGN-v2 §1); the
   others keep the same box so nothing reflows when the hour rolls over. */
.ns-hb-card {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    border-radius: clamp(18px, 3vmin, 40px);
    border: max(1px, 0.1vmin) solid transparent;
    background: transparent;
    padding: clamp(8px, 1.6vmin, 26px);
    transition: background-color 700ms ease, border-color 700ms ease;
    /* A display has no scrollbar and nobody to drag it; the list scrolls only
       so a long column is not silently cut off mid-row. */
    scrollbar-width: none;
    -ms-overflow-style: none;
}
.ns-hb-card::-webkit-scrollbar { width: 0; height: 0; display: none; }
.ns-hb-col-active .ns-hb-card {
    border-color: var(--hb-card-edge);
    background: var(--hb-card);
}

/* --- the habit list -------------------------------------------------------- */
.ns-hb-list {
    list-style: none;
    margin: 0;
    padding: 0;
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: clamp(10px, 2.2vmin, 34px);
    /* layout:'list' clips rather than scrolls — max_habits plus the "+N more"
       is the honest cap there. */
    overflow: hidden;
}
/* Inside a column card the list scrolls and starts at the top instead. */
.ns-hb-cols .ns-hb-list {
    justify-content: flex-start;
    overflow: visible;
    gap: clamp(8px, 1.8vmin, 28px);
}

.ns-hb-row {
    display: flex;
    align-items: center;
    gap: clamp(12px, 2.4vmin, 36px);
    min-width: 0;
}

/* The check glyph. A <button> when the slide is interactive AND today is one
   of this habit's scheduled days, an inert <span> otherwise — so a read-only
   wall display has literally nothing to press. */
.ns-hb-check {
    flex: 0 0 auto;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: clamp(30px, 5.4vmin, 84px);
    height: clamp(30px, 5.4vmin, 84px);
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: rgba(22, 74, 74, 0.28);
    color: var(--hb-empty);
    font: inherit;
    line-height: 1;
    transition: color 620ms ease, background-color 620ms ease;
}
button.ns-hb-check { cursor: pointer; -webkit-tap-highlight-color: transparent; }
button.ns-hb-check:focus-visible {
    outline: max(2px, 0.2vmin) solid var(--hb-done);
    outline-offset: clamp(2px, 0.3vmin, 5px);
}
.ns-hb-check-done {
    color: var(--hb-done);
    background: rgba(0, 166, 98, 0.16);
}
.ns-hb-check.ns-hb-busy { opacity: 0.65; }
.ns-hb-check.ns-hb-err {
    color: var(--hb-err);
    background: rgba(255, 90, 77, 0.2);
}

.ns-hb-check-svg {
    display: block;
    width: 72%;
    height: 72%;
    overflow: visible;
}
.ns-hb-check-ring {
    fill: none;
    stroke: currentColor;
    stroke-width: 2.4;
    opacity: 0.5;
}
.ns-hb-check-tick {
    fill: none;
    stroke: currentColor;
    stroke-width: 4.2;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-dasharray: 34;
    stroke-dashoffset: 34;
    transition: stroke-dashoffset 520ms cubic-bezier(0.215, 0.61, 0.355, 1);
}
.ns-hb-check-done .ns-hb-check-tick { stroke-dashoffset: 0; }

.ns-hb-body {
    flex: 1 1 auto;
    min-width: 0;
}

.ns-hb-name {
    display: block;
    width: 100%;
    margin: 0 0 clamp(4px, 0.9vmin, 14px) 0;
    padding: 0;
    border: 0;
    background: none;
    text-align: left;
    color: var(--hb-name);
    font-family: inherit;
    font-size: clamp(18px, 3.2vmin, 48px);
    font-weight: 600;
    letter-spacing: -0.01em;
    line-height: 1.2;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
button.ns-hb-name { cursor: pointer; -webkit-tap-highlight-color: transparent; }
button.ns-hb-name:focus-visible {
    outline: max(2px, 0.2vmin) solid var(--hb-done);
    outline-offset: clamp(2px, 0.3vmin, 5px);
}
.ns-hb-name.ns-hb-err { color: var(--hb-err); }
.ns-hb-name.ns-hb-busy { opacity: 0.65; }

/* ONE DOT PER SCHEDULED DAY — this row is the frequency, so a weekday habit
   is five dots wide and a Sunday habit one. It is deliberately not a fixed
   seven-column grid. */
.ns-hb-week {
    display: flex;
    align-items: flex-start;
    gap: clamp(8px, 1.8vmin, 28px);
}

.ns-hb-day {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: clamp(3px, 0.7vmin, 10px);
}

.ns-hb-dot {
    display: block;
    width: clamp(10px, 1.8vmin, 28px);
    height: clamp(10px, 1.8vmin, 28px);
    border-radius: 50%;
    background: var(--hb-empty);
    opacity: 0;
    transform: scale(0.4);
    transition: opacity 620ms ease,
                transform 620ms cubic-bezier(0.2, 0.8, 0.3, 1),
                background-color 900ms ease;
}

.ns-hb-letter {
    color: var(--hb-letter);
    font-size: clamp(9px, 1.4vmin, 22px);
    font-weight: 600;
    letter-spacing: 0.06em;
    line-height: 1;
}

/* The one-shot staggered fill-in. Every dot paints empty and invisible; the
   renderer adds .ns-hb-lit one frame later and each dot's own inline
   transition-delay cascades it in. This is NOT a setInterval on purpose — the
   reference's 200 ms repaint timer never clears and a display runs for days. */
.ns-hb-lit .ns-hb-dot { opacity: 1; transform: scale(1); }
.ns-hb-lit .ns-hb-dot-done { background: var(--hb-done); }
/* Scheduled but still ahead: same teal, 45%. Ordered after the rule above so
   it wins at equal specificity. */
.ns-hb-lit .ns-hb-dot-future { opacity: 0.45; }

/* --- §4.2 celebration ------------------------------------------------------
   One shot, pure keyframes, removed by the renderer on the BURST's own
   animationend. Nothing here runs unless a tap actually completed a habit. */
.ns-hb-check.ns-hb-pop {
    animation: ns-hb-pop 620ms cubic-bezier(0.2, 0.8, 0.3, 1);
}
@keyframes ns-hb-pop {
    0%   { transform: scale(1); }
    38%  { transform: scale(1.25); }
    100% { transform: scale(1); }
}

.ns-hb-burst {
    position: absolute;
    inset: 0;
    pointer-events: none;
    /* A no-op animation so the element has an animationend of its OWN to be
       reaped by, independent of the particles' own (which bubble). */
    animation: ns-hb-burst-life 720ms linear forwards;
}
@keyframes ns-hb-burst-life {
    from { opacity: 0.999; }
    to   { opacity: 1; }
}

.ns-hb-ring {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    border: max(1px, 0.2vmin) solid var(--hb-done);
    animation: ns-hb-ring-pulse 640ms ease-out forwards;
}
@keyframes ns-hb-ring-pulse {
    from { transform: scale(0.6); opacity: 0.85; }
    to   { transform: scale(2); opacity: 0; }
}

/* The <i> carries the inline rotate; its ::before does the outward travel, so
   the two transforms compose instead of overwriting each other. */
.ns-hb-spark {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 0;
    height: 0;
}
.ns-hb-spark::before {
    content: '';
    position: absolute;
    width: clamp(3px, 0.52vmin, 8px);
    height: clamp(3px, 0.52vmin, 8px);
    margin: calc(clamp(3px, 0.52vmin, 8px) / -2) 0 0 calc(clamp(3px, 0.52vmin, 8px) / -2);
    border-radius: 50%;
    background: var(--hb-done);
    animation: ns-hb-spark-fly 640ms cubic-bezier(0.2, 0.8, 0.3, 1) forwards;
}
@keyframes ns-hb-spark-fly {
    from { transform: translate3d(0, 0, 0) scale(1); opacity: 1; }
    to   { transform: translate3d(0, clamp(16px, 3vmin, 46px), 0) scale(0.2); opacity: 0; }
}

/* --- §4.3 detail overlay --------------------------------------------------- */
.ns-hb-overlay-host {
    position: absolute;
    inset: 0;
    z-index: 5;
    opacity: 0;
    transition: opacity 260ms ease;
}
.ns-hb-overlay-host.ns-hb-overlay-in { opacity: 1; }

.ns-hb-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: clamp(16px, 3vmin, 56px);
    background: rgba(0, 20, 22, 0.72);
    backdrop-filter: blur(clamp(6px, 1.4vmin, 22px));
    -webkit-backdrop-filter: blur(clamp(6px, 1.4vmin, 22px));
}

.ns-hb-sheet {
    display: flex;
    flex-direction: column;
    gap: clamp(10px, 2vmin, 30px);
    width: min(100%, 78vmin);
    max-height: 100%;
    overflow-y: auto;
    box-sizing: border-box;
    padding: clamp(14px, 2.6vmin, 42px);
    border-radius: clamp(18px, 3vmin, 40px);
    border: max(1px, 0.1vmin) solid var(--hb-card-edge);
    background: rgba(0, 43, 46, 0.86);
    transform: translate3d(0, clamp(8px, 1.4vmin, 20px), 0) scale(0.98);
    transition: transform 320ms cubic-bezier(0.2, 0.8, 0.3, 1);
    scrollbar-width: none;
    -ms-overflow-style: none;
}
.ns-hb-sheet::-webkit-scrollbar { width: 0; height: 0; display: none; }
.ns-hb-overlay-in .ns-hb-sheet { transform: translate3d(0, 0, 0) scale(1); }

.ns-hb-sheet-head {
    display: flex;
    align-items: center;
    gap: clamp(8px, 1.6vmin, 22px);
    min-width: 0;
}
.ns-hb-back {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    width: clamp(28px, 4.6vmin, 60px);
    height: clamp(28px, 4.6vmin, 60px);
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.08);
    color: var(--hb-name);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}
.ns-hb-back svg {
    width: 60%;
    height: 60%;
    fill: none;
    stroke: currentColor;
    stroke-width: 2.2;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.ns-hb-sheet-title {
    margin: 0;
    min-width: 0;
    color: var(--hb-name);
    font-size: clamp(18px, 3vmin, 44px);
    font-weight: 600;
    letter-spacing: -0.01em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ns-hb-cal { display: flex; flex-direction: column; gap: clamp(4px, 0.9vmin, 12px); }
.ns-hb-cal-month {
    color: rgba(255, 255, 255, 0.45);
    font-size: clamp(11px, 1.7vmin, 24px);
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}
.ns-hb-cal-heads,
.ns-hb-cal-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: clamp(3px, 0.7vmin, 10px);
}
.ns-hb-cal-head {
    text-align: center;
    color: var(--hb-letter);
    font-size: clamp(9px, 1.4vmin, 22px);
    font-weight: 600;
    letter-spacing: 0.06em;
}
.ns-hb-cal-cell {
    display: flex;
    align-items: center;
    justify-content: center;
    aspect-ratio: 1 / 1;
    border-radius: 50%;
    font-size: clamp(9px, 1.5vmin, 23px);
    font-weight: 600;
    color: rgba(255, 255, 255, 0.5);
    background: rgba(255, 255, 255, 0.05);
}
.ns-hb-cal-blank { background: none; }
.ns-hb-cal-off { color: rgba(255, 255, 255, 0.22); background: rgba(255, 255, 255, 0.03); }
.ns-hb-cal-done { color: #fff; background: var(--hb-done); }
.ns-hb-cal-fail { color: rgba(255, 255, 255, 0.55); background: var(--hb-empty); }
.ns-hb-cal-todo { color: rgba(255, 255, 255, 0.6); background: rgba(22, 74, 74, 0.5); }
.ns-hb-cal-current { box-shadow: 0 0 0 max(2px, 0.25vmin) var(--hb-fill); }

.ns-hb-streaks { display: flex; gap: clamp(10px, 2vmin, 30px); }
.ns-hb-streak {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    gap: clamp(2px, 0.4vmin, 6px);
    padding: clamp(8px, 1.5vmin, 22px);
    border-radius: clamp(12px, 2vmin, 26px);
    background: rgba(255, 255, 255, 0.06);
}
.ns-hb-streak-n {
    color: var(--hb-done);
    font-size: clamp(20px, 3.4vmin, 50px);
    font-weight: 700;
    line-height: 1;
}
.ns-hb-streak-k {
    color: rgba(255, 255, 255, 0.45);
    font-size: clamp(10px, 1.5vmin, 22px);
    font-weight: 600;
    letter-spacing: 0.06em;
}

.ns-hb-info { display: flex; flex-direction: column; }
.ns-hb-info-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: clamp(8px, 1.6vmin, 24px);
    padding: clamp(5px, 1vmin, 14px) 0;
    border-top: max(1px, 0.08vmin) solid rgba(255, 255, 255, 0.08);
}
.ns-hb-info-row:first-child { border-top: 0; }
.ns-hb-info-key {
    color: rgba(255, 255, 255, 0.45);
    font-size: clamp(11px, 1.7vmin, 25px);
    font-weight: 600;
}
.ns-hb-info-val {
    color: var(--hb-name);
    font-size: clamp(11px, 1.7vmin, 25px);
    font-weight: 600;
    text-align: right;
}

/* --- narrow / portrait -----------------------------------------------------
   A portrait panel cannot hold three columns side by side at a legible size,
   so the grid turns 90 degrees: EVERY time-of-day period that was rendered
   stays on screen, stacked as rows. Hiding two of them (which this rule used
   to do) loses the whole point of the board -- the owner should still see
   their whole day. Periods with no habits are dropped by the renderer, not
   hidden here (owner decision, 2026-09-20).

   Each row is its own scroll area, so one crowded period cannot push the other
   two off the bottom. `grid-template-rows: repeat(3, minmax(0, 1fr))` with
   `min-height: 0` on the track is what lets the inner scrollers actually
   scroll instead of the rows growing to fit their content. */
@media (orientation: portrait), (max-width: 760px) {
    .ns-hb-grid {
        grid-template-columns: 1fr;
        /* Fallback for a browser that will not substitute a custom property
           into a track list: three equal rows, which is the all-open case. */
        grid-template-rows: repeat(3, minmax(0, 1fr));
        --hb-row-morning: minmax(0, 1fr);
        --hb-row-afternoon: minmax(0, 1fr);
        --hb-row-evening: minmax(0, 1fr);
        grid-template-rows: var(--hb-row-morning) var(--hb-row-afternoon) var(--hb-row-evening);
        gap: clamp(10px, 2vmin, 28px);
    }
    /* The data-cols rules above are plain selectors -- a media query adds no
       specificity, so without this they would out-specify the single column. */
    .ns-hb-grid[data-cols] { grid-template-columns: 1fr; }
    /* ONE ROW PER RENDERED PERIOD (owner decision, 2026-09-20). `data-slots`
       is the first letter of each period the renderer actually drew, in slot
       order, so the track list names exactly the tracks that have a section in
       them -- a two-period stack gets two rows, not two rows and a phantom.
       The three-period case is the base rule above. Exact-match rules rather
       than :has(), for the same reason data-shut uses substrings, and the
       shut rules below still size whichever of these tracks exist. */
    .ns-hb-grid[data-slots="m"] { grid-template-rows: var(--hb-row-morning); }
    .ns-hb-grid[data-slots="a"] { grid-template-rows: var(--hb-row-afternoon); }
    .ns-hb-grid[data-slots="e"] { grid-template-rows: var(--hb-row-evening); }
    .ns-hb-grid[data-slots="ma"] {
        grid-template-rows: var(--hb-row-morning) var(--hb-row-afternoon);
    }
    .ns-hb-grid[data-slots="me"] {
        grid-template-rows: var(--hb-row-morning) var(--hb-row-evening);
    }
    .ns-hb-grid[data-slots="ae"] {
        grid-template-rows: var(--hb-row-afternoon) var(--hb-row-evening);
    }
    /* A period that is folded shut gets a content-sized track -- its header
       and nothing else -- so it hands its share of a tall panel back to the
       periods still open. `auto` also means the track follows the body's
       height while it animates, so the siblings grow smoothly rather than
       jumping. Three composable substring rules instead of :has(), which is
       not on every display browser; data-shut="" matches none of them. */
    .ns-hb-grid[data-shut*="m"] { --hb-row-morning: auto; }
    .ns-hb-grid[data-shut*="a"] { --hb-row-afternoon: auto; }
    .ns-hb-grid[data-shut*="e"] { --hb-row-evening: auto; }
    /* A period that is already behind the wall clock rests collapsed: the
       owner wants to see what is still coming, not what is gone. The header,
       its done/total summary and the chevron all stay, so a collapsed period
       can never be mistaken for an empty one. */
    .ns-hb-col-shut .ns-hb-col-body {
        grid-template-rows: 0fr;
        opacity: 0;
    }
    /* With the body folded to nothing the header-to-body gap would be a strip
       of dead space under every collapsed period. */
    .ns-hb-cols .ns-hb-col-shut { gap: 0; }
    .ns-hb-col-sum { display: block; }
    .ns-hb-chev { display: block; }
    button.ns-hb-col-head { cursor: pointer; }
    .ns-hb-cols .ns-hb-col {
        display: flex;
        min-height: 0;
    }
    /* The active period keeps its full-opacity card; the other two stay
       readable rather than dropping to the landscape 0.5, because on a tall
       panel they are the only other thing on screen. */
    .ns-hb-cols .ns-hb-col { opacity: 0.72; }
    .ns-hb-cols .ns-hb-col-active { opacity: 1; }
    /* Header and title sit inline on a row so each period spends its height on
       habits rather than on chrome. */
    .ns-hb-col-head { gap: clamp(6px, 1.6vmin, 14px); }
}

@media (prefers-reduced-motion: reduce) {
    .ns-hb-dot,
    .ns-hb-check,
    .ns-hb-check-tick,
    .ns-hb-card,
    .ns-hb-col,
    .ns-hb-col-body,
    .ns-hb-chev,
    .ns-hb-bar-fill,
    .ns-hb-overlay-host,
    .ns-hb-sheet { transition: none; }
    /* The background freezes to a static gradient rather than drifting. */
    .ns-hb-blob { animation: none; }
    /* The celebration reduces to the colour change alone. */
    .ns-hb-check.ns-hb-pop { animation: none; }
    .ns-hb-burst { display: none; }
}

/* Sci-fi neon: same structure, the skin from styles.css .ns-style-scifi_neon.
   Both classes sit on the SAME element (see habitsSlideHtml), so these are
   compound selectors, not descendant ones. */
.ns-style-scifi_neon.ns-hb {
    --hb-done: #00F0FF;
    --hb-empty: #12414D;
    --hb-letter: rgba(0, 240, 255, 0.55);
    --hb-name: #DFF9FF;
    --hb-ink: #03070f;
    --hb-blob-a: #00F0FF;
    --hb-blob-b: #0b2f45;
    --hb-blob-c: #1268a8;
    --hb-track: #0d3d4a;
    --hb-fill: #00F0FF;
    --hb-card: rgba(0, 240, 255, 0.05);
    --hb-card-edge: rgba(0, 240, 255, 0.28);
}
.ns-style-scifi_neon.ns-hb .ns-hb-check {
    background: rgba(0, 240, 255, 0.05);
}
.ns-style-scifi_neon.ns-hb .ns-hb-check-done {
    background: rgba(0, 240, 255, 0.12);
    filter: drop-shadow(0 0 clamp(4px, 0.9vmin, 14px) rgba(0, 240, 255, 0.6));
}
.ns-style-scifi_neon.ns-hb .ns-hb-bar-fill {
    box-shadow: 0 0 clamp(5px, 1vmin, 16px) rgba(0, 240, 255, 0.7);
}
.ns-style-scifi_neon.ns-hb-lit .ns-hb-dot-done {
    box-shadow: 0 0 clamp(6px, 1.2vmin, 18px) rgba(0, 240, 255, 0.65);
}
