/* Refaced "Midnight Glass" theme (docs/SPEC-refaced-visual-refresh.md).

   EVERY rule in this file starts with .rf-theme, the class base.html puts on <body> only for a
   converted page. Nothing here touches :root and nothing here names an IRIS class, so this file
   cannot reach an IRIS page or a page still on the classic look. test_refaced_theme.py reads this
   file and fails on the first selector that breaks that rule.

   The token values are LOCKED (spec, "Locked tokens", confirmed final 2026-09-18). The same
   values are contrast-checked in test_refaced_theme.py; change one here and that test has to
   agree it is still readable. */

/* The TRUE italic of the app's display serif, for the one line that uses it (the ring's "Day n of
   7 this week"). The app ships the upright face only (static/css/app-fonts.css), so without this a
   browser slants the upright letters and calls it italic. Declared HERE, not in app-fonts.css,
   because this stylesheet is only linked on a Refaced page: nothing on a classic page changes.
   Same source as the upright files (the woff2 subsets Google Fonts serves, SIL OFL, licence in
   static/fonts/OFL-cormorant-garamond.txt), downloaded 2026-09-19 with Melodie's OK. */
@font-face {
    font-family: 'Cormorant Garamond';
    font-style: italic;
    font-weight: 500 700;
    font-display: swap;
    src: url(../fonts/cormorant-garamond-italic-latin.woff2) format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Cormorant Garamond';
    font-style: italic;
    font-weight: 500 700;
    font-display: swap;
    src: url(../fonts/cormorant-garamond-italic-latin-ext.woff2) format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

.rf-theme {
    --rf-base: #170D19;
    --rf-blob: #4A1D48;
    --rf-glow: #E2BF6B;
    /* The accent where it is a SHAPE (the ring's arc, a filled shield) rather than a word.
       Identical to --rf-glow in this palette; the light one needs them to differ, because a
       colour can be legible as a 40px arc and illegible as 11px lettering. */
    --rf-graphic: var(--rf-glow);
    --rf-action: #DE1B87;
    --rf-text: #F4ECF1;
    --rf-text-muted: rgba(244, 236, 241, .66);
    --rf-glass-bg: rgba(255, 255, 255, .07);
    --rf-glass-border: rgba(255, 255, 255, .14);
    --rf-glass-shadow: 0 24px 50px -28px rgba(0, 0, 0, .8), inset 0 1px 0 rgba(255, 255, 255, .12);
    /* The slide-over panel sits on top of page content, where a 7% glass would be unreadable. */
    --rf-panel-bg: #21132A;
    /* The small labels (the date, "Your progress", "Longest streak"...). IBM Plex Mono capitals in
       the mockup ("not feminine enough"), then an italic ("sending the wrong message"); Melodie's
       pick, 2026-09-19: the app's own display serif, upright, in capitals, with a LARGER first
       letter on each main word. The larger letters are real markup (the `rf_label` filter wraps
       them in .rf-cap), not the browser's synthetic small caps. */
    --rf-font-label: var(--font-display);

    /* How much room the floating tab bar takes at the bottom of the screen. Everything that must
       not sit under it (the footer, the feedback button, the cookie banner) measures from this. */
    --rf-tabbar-height: 60px;
    --rf-tabbar-gap: 14px;
    --rf-tabbar-clearance: calc(var(--rf-tabbar-height) + var(--rf-tabbar-gap) + env(safe-area-inset-bottom, 0px));

    /* One pink app-wide: the classic buttons that still appear inside the shell (the cookie
       banner's Accept) take IRIS's pink too, rather than keeping the old #fc3cad beside it. */
    --primary-color: #DE1B87;
    --primary-strong: #B5126C;

    background: var(--rf-base);
    color: var(--rf-text);
    /* Scrollbars and form controls drawn by the browser go dark too. */
    color-scheme: dark;
}

/* The page behind the open account panel stays put. */
.rf-theme:has(.rf-panel[open]) {
    overflow: hidden;
}

/* The classic page's faint grid overlay. */
.rf-theme::before {
    display: none;
}

/* The classic stylesheet colours every bare <p> a dark grey (`p { color: var(--text-medium) }`),
   which is invisible on this base. A paragraph here takes the colour of whatever it sits in: the
   page's light text, or a classic white card's own dark text (the cookie banner), which is why
   this inherits rather than naming a colour. */
.rf-theme p {
    color: inherit;
}

/* ── Atmosphere: three large blurred shapes behind everything ───────────────────────────── */
.rf-theme .rf-atmosphere {
    position: fixed;
    inset: 0;
    overflow: hidden;
    pointer-events: none;
    z-index: -1;
}

.rf-theme .rf-atmosphere span {
    position: absolute;
    border-radius: 50%;
}

.rf-theme .rf-atmosphere span:nth-child(1) {
    width: max(440px, 38vw);
    height: max(440px, 38vw);
    left: -160px;
    top: -120px;
    background: var(--rf-blob);
    filter: blur(70px);
    opacity: .9;
}

.rf-theme .rf-atmosphere span:nth-child(2) {
    width: max(300px, 26vw);
    height: max(300px, 26vw);
    right: -120px;
    top: 260px;
    background: var(--rf-glow);
    filter: blur(90px);
    opacity: .22;
}

.rf-theme .rf-atmosphere span:nth-child(3) {
    width: max(420px, 34vw);
    height: max(420px, 34vw);
    left: -80px;
    bottom: -140px;
    background: var(--rf-blob);
    filter: blur(90px);
    opacity: .6;
}

/* ── Shared pieces ──────────────────────────────────────────────────────────────────────── */
.rf-theme .rf-glass {
    background: var(--rf-glass-bg);
    border: 1px solid var(--rf-glass-border);
    -webkit-backdrop-filter: blur(20px) saturate(150%);
    backdrop-filter: blur(20px) saturate(150%);
    box-shadow: var(--rf-glass-shadow);
}

.rf-theme .rf-card {
    border-radius: 30px;
    padding: 22px 20px;
}

.rf-theme .rf-eyebrow {
    font-family: var(--rf-font-label);
    font-size: 12px;
    font-style: normal;
    font-weight: 600;
    letter-spacing: .1em;
    line-height: 1.3;
    text-transform: uppercase;
    /* Cormorant's default figures are old-style (lowercase height); in a line of capitals a "19"
       or a "2" looked shrunken. Lining figures stand as tall as the capitals. */
    font-variant-numeric: lining-nums;
    color: var(--rf-glow);
}

/* A label's larger first letters. line-height 0 so a taller letter never pushes its line apart. */
.rf-theme .rf-cap {
    font-size: 1.38em;
    letter-spacing: .04em;
    line-height: 0;
}

.rf-theme .rf-eyebrow.is-muted,
.rf-theme .rf-muted {
    color: var(--rf-text-muted);
}

.rf-theme .rf-display {
    font-family: var(--font-display);
    font-weight: 600;
    letter-spacing: -.01em;
    line-height: 1.05;
    color: var(--rf-text);
}

.rf-theme .rf-btn-primary {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    min-height: 56px;
    padding: 0 28px;
    border: 0;
    border-radius: 999px;
    background: var(--rf-action);
    color: #fff;
    font: 700 16px var(--font-sans);
    text-decoration: none;
    cursor: pointer;
    box-shadow: 0 16px 36px -14px var(--rf-action);
}

.rf-theme .rf-btn-primary.rf-btn-block {
    display: flex;
    width: 100%;
}

.rf-theme .rf-btn-primary.rf-btn-small {
    min-height: 44px;
    padding: 0 18px;
    font-size: 14px;
}

.rf-theme .rf-pill {
    display: inline-block;
    padding: 6px 16px;
    border-radius: 999px;
    background: var(--rf-action);
    color: #fff;
    font: 700 12px var(--font-sans);
}

.rf-theme .rf-badge {
    display: inline-grid;
    place-items: center;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: 999px;
    background: var(--rf-action);
    color: #fff;
    font: 700 11px var(--font-sans);
}

/* A visible keyboard focus ring on every interactive Refaced piece. Gold on the dark base, the
   same job .ir-btn:focus-visible does on the IRIS pages. */
.rf-theme .rf-btn-primary:focus-visible,
.rf-theme .rf-avatar:focus-visible,
.rf-theme .rf-brand:focus-visible,
.rf-theme .rf-tab:focus-visible,
.rf-theme .rf-panel a:focus-visible,
.rf-theme .rf-panel button:focus-visible,
.rf-theme .rf-panel summary:focus-visible {
    outline: 2px solid var(--rf-glow);
    outline-offset: 3px;
}

/* ── Header ─────────────────────────────────────────────────────────────────────────────── */
.rf-theme .rf-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: min(var(--container), calc(100% - 2.5rem));
    margin: 0 auto;
    padding: 16px 0 6px;
}

.rf-theme .rf-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    text-decoration: none;
    border-radius: 12px;
}

.rf-theme .rf-brand-mark {
    width: 34px;
    height: 34px;
    object-fit: contain;
    flex: none;
}

.rf-theme .rf-brand-text {
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.rf-theme .rf-brand-name {
    font-family: var(--font-display);
    font-size: 22px;
    font-weight: 600;
    line-height: 1;
    letter-spacing: -.01em;
    color: var(--rf-text);
}

.rf-theme .rf-brand-tagline {
    font-family: var(--rf-font-label);
    font-variant-numeric: lining-nums;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .1em;
    line-height: 1.1;
    text-transform: uppercase;
    color: var(--rf-text-muted);
}

.rf-theme .rf-avatar {
    position: relative;
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    padding: 0;
    border-radius: 999px;
    color: var(--rf-text);
    cursor: pointer;
}

.rf-theme .rf-avatar-initial {
    font-family: var(--font-display);
    font-size: 18px;
    font-weight: 700;
    line-height: 1;
}

.rf-theme .rf-avatar-badge {
    position: absolute;
    top: -4px;
    right: -4px;
}

/* On a page with NO floating tab bar the header is the ONLY navigation, and the Refaced header
   is `position: static` while the classic one is `sticky; top: 0`. On every other converted page
   the bar is what a member reaches for once she has scrolled, so static costs nothing there; on
   a page with neither there is nothing to tap through the whole middle of a long lesson. An
   adversarial reviewer measured exactly that on 2026-09-19: at 390x844, scrolled 45% down a
   lesson, nothing on screen led anywhere.

   This is not a new design, it is the behaviour the CLASSIC page already has, which is the
   standing rule. Same z-index as classic's header (200), so the celebrate banner still covers it.

   Its own list (`config.REFACED_STICKY_HEADER_ENDPOINTS`), NOT every page with no tab bar: the
   first version keyed on `rf-no-tabbar` and so changed the ROUTINE player, which has no bar
   either. A reviewer measured that page scrolling at every phone size (1199px of content in an
   844px window at 390 wide), so the header would have drawn over the top of its stage, on a page
   that had already converged over four review rounds, with its own guard unable to see it. */
.rf-theme.rf-sticky-header .rf-header {
    position: sticky;
    top: 0;
    z-index: 200;
    width: 100%;
    padding-left: max(calc((100% - var(--container)) / 2), 1.25rem);
    padding-right: max(calc((100% - var(--container)) / 2), 1.25rem);
    background: linear-gradient(var(--rf-glass-bg), var(--rf-glass-bg)), rgba(23, 13, 25, .88);
    -webkit-backdrop-filter: blur(20px) saturate(150%);
    backdrop-filter: blur(20px) saturate(150%);
    border-bottom: 1px solid var(--rf-glass-border);
}

/* ── Account panel (a <dialog>, sliding in from the right) ──────────────────────────────── */
.rf-theme .rf-panel {
    position: fixed;
    inset: 0 0 0 auto;
    width: min(340px, 88vw);
    height: 100%;
    max-height: none;
    margin: 0;
    padding: 0;
    border: 0;
    background: var(--rf-panel-bg);
    color: var(--rf-text);
    /* The BODY scrolls, not the dialog: a click on the dialog's own scrollbar has the dialog as
       its target, which is how a backdrop click is recognised, and would close the panel. */
    overflow: hidden;
    z-index: 1000;
}

/* A browser with no <dialog> support draws it as an ordinary box, open or not. */
.rf-theme .rf-panel:not([open]) {
    display: none;
}

.rf-theme .rf-panel[open] {
    animation: rfPanelIn .22s ease-out;
}

.rf-theme .rf-panel::backdrop {
    background: rgba(10, 5, 12, .62);
}

@keyframes rfPanelIn {
    from { transform: translateX(100%); }
    to { transform: translateX(0); }
}

@media (prefers-reduced-motion: reduce) {
    .rf-theme .rf-panel[open] { animation: none; }
    .rf-theme .rf-panel-admin > summary::after { transition: none; }
}

.rf-theme .rf-panel-body {
    display: flex;
    flex-direction: column;
    height: 100%;
    overflow-y: auto;
    padding: 20px 20px calc(24px + env(safe-area-inset-bottom, 0px));
    /* The panel's edge line lives HERE, not on the dialog: the dialog must have no border and no
       padding, so that a click whose target is the dialog can only be a click on the backdrop. */
    border-left: 1px solid var(--rf-glass-border);
}

.rf-theme .rf-panel-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-bottom: 12px;
}

.rf-theme .rf-panel-close {
    width: 44px;
    height: 44px;
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: var(--rf-text);
    font-size: 26px;
    line-height: 1;
    cursor: pointer;
}

.rf-theme .rf-panel-links {
    display: flex;
    flex-direction: column;
}

.rf-theme .rf-panel-links > a,
.rf-theme .rf-panel-admin > summary {
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: 52px;
    border-bottom: 1px solid rgba(255, 255, 255, .1);
    color: var(--rf-text);
    font: 600 16px var(--font-sans);
    text-decoration: none;
    cursor: pointer;
}

.rf-theme .rf-panel-links > a[aria-current="page"] {
    color: var(--rf-glow);
}

.rf-theme .rf-panel-admin > summary {
    list-style: none;
}

.rf-theme .rf-panel-admin > summary::-webkit-details-marker {
    display: none;
}

.rf-theme .rf-panel-admin > summary::after {
    content: "";
    width: 8px;
    height: 8px;
    margin-left: auto;
    border-right: 2px solid var(--rf-glow);
    border-bottom: 2px solid var(--rf-glow);
    transform: rotate(-45deg);
    transition: transform .15s ease;
}

.rf-theme .rf-panel-admin[open] > summary::after {
    transform: rotate(45deg);
}

/* The admin links stay dense on purpose: they are for getting work done quickly. */
.rf-theme .rf-panel-admin-links {
    display: flex;
    flex-direction: column;
    padding: 6px 0 10px;
    border-bottom: 1px solid rgba(255, 255, 255, .1);
}

.rf-theme .rf-panel-admin-links .nav-dropdown-heading {
    display: block;
    padding: 12px 0 4px;
    font-family: var(--rf-font-label);
    font-size: 12px;
    font-style: normal;
    font-weight: 600;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--rf-text-muted);
    background: none;
    border: 0;
}

.rf-theme .rf-panel-admin-links a {
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: 38px;
    padding: 0 0 0 10px;
    color: var(--rf-text);
    font: 500 14px var(--font-sans);
    text-decoration: none;
    background: none;
}

.rf-theme .rf-panel-admin-links a.nav-active {
    color: var(--rf-glow);
}

.rf-theme .rf-panel-signout {
    margin-top: auto;
    padding-top: 20px;
}

.rf-theme .rf-panel-signout button {
    width: 100%;
    min-height: 48px;
    border: 1px solid var(--rf-glass-border);
    border-radius: 999px;
    background: transparent;
    color: var(--rf-text);
    font: 600 15px var(--font-sans);
    cursor: pointer;
}

/* ── Bottom tab bar ─────────────────────────────────────────────────────────────────────── */
.rf-theme .rf-tabbar {
    position: fixed;
    left: 50%;
    bottom: calc(var(--rf-tabbar-gap) + env(safe-area-inset-bottom, 0px));
    transform: translateX(-50%);
    z-index: 800;
    display: flex;
    width: min(440px, calc(100% - 32px));
    min-height: var(--rf-tabbar-height);
    padding: 4px 6px;
    border-radius: 26px;
    /* Page content scrolls underneath this bar, so it carries a tint of the base colour under the
       glass: the locked 7% white alone lets bright content read straight through the icons. */
    background: linear-gradient(var(--rf-glass-bg), var(--rf-glass-bg)), rgba(23, 13, 25, .72);
}

.rf-theme .rf-tab {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    padding: 8px 0;
    border-radius: 20px;
    color: var(--rf-text-muted);
    font: 600 11px var(--font-sans);
    text-decoration: none;
}

.rf-theme .rf-tab.is-active {
    color: var(--rf-text);
}

/* Only the active tab DRAWS its label; the others keep theirs for a screen reader. */
.rf-theme .rf-tab:not(.is-active) .rf-tab-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
}

/* ── Classic pieces that still render inside the shell ──────────────────────────────────── */
.rf-theme .main-content {
    padding-bottom: 2rem;
}

.rf-theme .site-footer {
    padding-bottom: calc(var(--rf-tabbar-clearance) + 1.5rem);
}

.rf-theme .footer-shell {
    border-top-color: rgba(255, 255, 255, .1);
    color: var(--rf-text-muted);
}

/* The footer's links are legally required (privacy, terms, the two health-data policies, SMS
   terms, Do Not Sell). base.html dims each one with an INLINE opacity: .8, which on top of the
   muted colour dropped them to 4.3:1 over an atmosphere shape. !important is the only thing that
   outranks an inline style; the alternative was editing seven links in the classic footer. */
.rf-theme .site-footer a {
    opacity: 1 !important;
}

/* Flash messages and the welcome-back banner. The classic versions are dark ink on a see-through
   tint, written for a white page: on this base they measured between 1.1:1 and 2.4:1. Here each
   one is a glass card with the page's own light text, told apart by the stripe on its left. */
.rf-theme .alert,
.rf-theme .welcome-back-banner {
    background: var(--rf-glass-bg);
    border: 1px solid var(--rf-glass-border);
    border-left: 3px solid #7ED9A0;
    color: var(--rf-text);
    box-shadow: none;
}

.rf-theme .alert-info {
    border-left-color: var(--rf-glow);
}

.rf-theme .alert-error {
    border-left-color: #FF8A80;
}

.rf-theme .welcome-back-banner {
    border-left-color: var(--rf-action);
}

.rf-theme .welcome-back-close {
    color: var(--rf-text);
}

/* The "Do Not Sell" confirmation toast pops up from the bottom edge, where the tab bar now is. */
.rf-theme .ftfl-toast {
    bottom: calc(var(--rf-tabbar-clearance) + 12px);
}

/* The floating Feedback button itself is unchanged; it only moves up so it never sits on the
   tab bar, at every width the bar shows (the classic rule lifts it on phones alone). */
.rf-theme .beta-fab-wrap {
    bottom: calc(var(--rf-tabbar-clearance) + 12px);
}

/* The cookie banner is a required notice. It keeps its own white card and dark text exactly as
   they are; it only floats ABOVE the tab bar instead of covering it, so a visitor who has not
   chosen yet can still move around the app. */
.rf-theme .consent-banner {
    left: 12px;
    right: 12px;
    bottom: calc(var(--rf-tabbar-clearance) + 10px);
    border-radius: 18px;
    color: #101018;
}

/* Keyboard focus INSIDE the cookie banner (Melodie, 2026-09-19: "give the banner's buttons a
   clearly visible focus ring"). The page's `color-scheme: dark` turns the browser's DEFAULT ring
   a light blue, which is right on the dark page and about 1.7:1 on this WHITE card: a keyboard
   user could not see which consent choice she was on. The ring is the banner's own dark ink
   (no new colour), 3px, held off the control by a 2px gap so it reads around the pink Accept
   button too. Every new-look page, not only the player: the cause is the same on all of them. */
.rf-theme .consent-banner a:focus-visible,
.rf-theme .consent-banner button:focus-visible {
    outline: 3px solid #101018;
    outline-offset: 2px;
}

/* ══ Shared pieces added with the Dashboard (step 2) ═══════════════════════════════════════ */

.rf-theme .rf-hr {
    height: 1px;
    margin: 0;
    border: 0;
    background: rgba(255, 255, 255, .1);
}

.rf-theme .rf-chip {
    padding: 2px 9px;
    border-radius: 999px;
    background: rgba(255, 255, 255, .1);
    color: var(--rf-text);
    font: 600 11px var(--font-sans);
    letter-spacing: 0;
    text-transform: none;
}

.rf-theme .rf-pill.is-soon {
    background: rgba(255, 255, 255, .12);
    color: var(--rf-text);
}

/* The progress ring: a track, a gold arc with a soft glow, a dot marking where it starts. */
.rf-theme .rf-loop {
    position: relative;
    width: 140px;
    height: 140px;
    flex: none;
}

.rf-theme .rf-loop svg {
    display: block;
    width: 100%;
    height: 100%;
    /* The arc's glow reaches past the ring; clipped to the box it shows a square edge. */
    overflow: visible;
}

.rf-theme .rf-loop-track,
.rf-theme .rf-loop-arc {
    fill: none;
    stroke-width: 10;
}

.rf-theme .rf-loop-track {
    stroke: rgba(255, 255, 255, .1);
}

.rf-theme .rf-loop-arc {
    stroke: var(--rf-glow);
    stroke-linecap: round;
    filter: drop-shadow(0 0 8px var(--rf-glow));
}

.rf-theme .rf-loop-start {
    fill: var(--rf-glow);
}

.rf-theme .rf-loop-centre {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
}

.rf-theme .rf-loop-number {
    font-family: var(--font-display);
    font-size: 44px;
    font-weight: 600;
    line-height: 1;
    color: var(--rf-text);
}

.rf-theme .rf-loop-unit {
    font-family: var(--rf-font-label);
    font-variant-numeric: lining-nums;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--rf-text-muted);
}

.rf-theme .rf-shield {
    width: 20px;
    height: 20px;
    flex: none;
    fill: none;
    stroke: rgba(255, 255, 255, .3);
    stroke-width: 1.4;
}

.rf-theme .rf-shield.is-banked {
    fill: var(--rf-glow);
    stroke: none;
}

/* The bank's row of shields, one per freeze it can hold. An empty slot keeps the outline above
   rather than a dimmed fill: a faded shield beside a lit one reads as a state, not as a count. */
.rf-theme .rfd-shields {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    flex: none;
}

/* Beside the shields the sentence had a third of the card's width and broke over three lines.
   Stacked, it gets the whole column. */
.rf-theme .rfd-freeze--bank {
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
}

/* A tile: a signature routine. Allison's covers are 720 x 1280 (9:16), made for the first
   version of this app, and each one SAYS the routine's name. So the frame is exactly 9:16 (the
   art is never cropped), nothing is written across it, and the name is only printed when there
   is no cover to say it. */
.rf-theme .rf-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding: 6px 6px 12px;
    border-radius: 20px;
    color: var(--rf-text);
    text-align: center;
    text-decoration: none;
    min-width: 0;
}

/* A Coming-soon tile is quieter, but only its picture is dimmed: opacity on the whole tile would
   drag the words' contrast down with it, and nothing inside can climb back out of that. */
.rf-theme .rf-tile.is-quiet .rf-tile-cover {
    opacity: .6;
}

.rf-theme .rf-tile-art {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    aspect-ratio: 9 / 16;
    padding: 8px;
    border-radius: 15px;
    overflow: hidden;
    background: rgba(255, 255, 255, .08);
}

.rf-theme .rf-tile-cover {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: contain;
}

/* In the corner, not the middle: the middle of her cover art is the routine's name. */
.rf-theme .rf-tile-lock {
    position: absolute;
    right: 6px;
    bottom: 6px;
    width: 30px;
    height: 30px;
    padding: 7px;
    border-radius: 50%;
    background: rgba(23, 13, 25, .72);
    color: var(--rf-text);
}

/* The Spotlight's free-claim tile (iris_freebies.py) is a button inside a form: it fills its grid
   cell and looks exactly like the link tiles beside it. */
.rf-theme .rfd-claim {
    display: flex;
    min-width: 0;
    margin: 0;
}

.rf-theme .rfd-claim .rf-tile {
    width: 100%;
    border: 0;
    font: inherit;
    cursor: pointer;
}

.rf-theme .rfd-claim .rf-tile:focus-visible {
    outline: 3px solid var(--rf-graphic);
    outline-offset: 2px;
}

/* Its countdown: a small pill across the top of the picture. */
.rf-theme .rf-tile-countdown {
    position: absolute;
    top: 6px;
    left: 50%;
    transform: translateX(-50%);
    padding: 3px 9px;
    border-radius: 999px;
    background: rgba(23, 13, 25, .78);
    color: #fff;
    font: 700 11px var(--font-sans);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.rf-theme .rf-tile-name {
    position: relative;
    max-width: 100%;
    font-size: 18px;
    line-height: 1.1;
    /* Whole words where they fit ("Monthly Spotlight" used to break mid-word); a single word too
       long for the frame breaks rather than being clipped on both sides. */
    overflow-wrap: break-word;
}

.rf-theme .rf-tile-meta {
    font-size: 11px;
    letter-spacing: .08em;
    line-height: 1.3;
    color: var(--rf-text-muted);
}

.rf-theme .rf-tile .rf-pill {
    margin-top: auto;
    padding: 6px 10px;
    white-space: nowrap;
}

/* A row: a saved routine, or any "thumbnail, two lines, action" line. */
.rf-theme .rf-row {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 6px 6px 6px 12px;
    border-radius: 20px;
    color: var(--rf-text);
    text-decoration: none;
}

.rf-theme .rf-row-main {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 12px;
    min-height: 56px;
    color: var(--rf-text);
    text-decoration: none;
    border-radius: 14px;
}

.rf-theme .rf-row-thumb {
    width: 44px;
    height: 44px;
    flex: none;
    border-radius: 14px;
    overflow: hidden;
    background: rgba(255, 255, 255, .12);
}

.rf-theme .rf-row-thumb video {
    width: 100%;
    height: 100%;
    object-fit: cover;
    pointer-events: none;
}

.rf-theme .rf-row-text {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.rf-theme .rf-row-title {
    font: 600 15px var(--font-sans);
    overflow-wrap: anywhere;
}

.rf-theme .rf-row-sub {
    font-size: 12px;
    line-height: 1.4;
}

.rf-theme .rf-row-play {
    flex: none;
    margin: 0 6px;
}

.rf-theme .rf-row-edit {
    width: 44px;
    height: 44px;
    flex: none;
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: var(--rf-text-muted);
    font-size: 16px;
    cursor: pointer;
}

/* A fold: a whole page's worth of content behind one tap. A native <details>, so it works with
   a keyboard and with no script. Progress photos will use the same one in pass B. */
.rf-theme .rf-fold {
    border-radius: 24px;
    overflow: hidden;
}

.rf-theme .rf-fold > summary {
    display: flex;
    align-items: center;
    gap: 12px;
    min-height: 56px;
    padding: 12px 18px;
    list-style: none;
    cursor: pointer;
    color: var(--rf-text);
}

.rf-theme .rf-fold > summary::-webkit-details-marker {
    display: none;
}

.rf-theme .rf-fold > summary::after {
    content: "";
    width: 8px;
    height: 8px;
    margin-left: auto;
    flex: none;
    border-right: 2px solid var(--rf-glow);
    border-bottom: 2px solid var(--rf-glow);
    transform: rotate(-45deg);
}

.rf-theme .rf-fold[open] > summary::after {
    transform: rotate(45deg);
}

.rf-theme .rf-fold-icon {
    flex: none;
    color: var(--rf-glow);
}

.rf-theme .rf-fold-title {
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.rf-theme .rf-fold-title .rf-eyebrow {
    color: var(--rf-text);
}

.rf-theme .rf-fold-sub {
    font-size: 11px;
}

.rf-theme .rf-fold-body {
    padding: 4px 18px 18px;
}

.rf-theme .rf-info-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    flex: none;
    padding: 0;
    border: 1px solid rgba(244, 236, 241, .5);
    border-radius: 50%;
    background: transparent;
    color: var(--rf-text);
    font: 700 13px var(--rf-font-label);
    cursor: pointer;
    /* 18px to the eye, 44px to a thumb. */
    position: relative;
}

.rf-theme .rf-info-btn::before {
    content: "";
    position: absolute;
    inset: -13px;
}

.rf-theme .rf-credit {
    position: relative;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 14px 0 4px;
}

.rf-theme .rf-credit-lock {
    color: var(--rf-glow);
}

.rf-theme .rf-credit-label {
    font-size: 13px;
}

.rf-theme .rf-credit-amount {
    margin-left: auto;
    font: 600 21px var(--font-display);
}

.rf-theme .rf-credit-state {
    font-size: 11px;
}

.rf-theme .rf-info-pop {
    position: absolute;
    left: 0;
    right: 0;
    top: 48px;
    z-index: 5;
    padding: 14px 16px;
    border-radius: 16px;
    background: var(--rf-panel-bg);
    font-size: 12px;
    line-height: 1.5;
}

.rf-theme .rf-info-pop[hidden] {
    display: none;
}

.rf-theme .rf-info-close {
    min-height: 44px;
    margin-top: 4px;
    padding: 0;
    border: 0;
    background: none;
    color: var(--rf-glow);
    font: 600 12px var(--font-sans);
    cursor: pointer;
}

.rf-theme .rf-tile:focus-visible,
.rf-theme a.rf-row:focus-visible,
.rf-theme .rfd-build-link:focus-visible,
.rf-theme .rf-row-main:focus-visible,
.rf-theme .rf-row-edit:focus-visible,
.rf-theme .rf-fold > summary:focus-visible,
.rf-theme .rf-info-btn:focus-visible,
.rf-theme .rf-info-close:focus-visible,
.rf-theme .activity-cell[data-date]:focus-visible,
.rf-theme .day-detail-close:focus-visible,
.rf-theme .home-screen-hint summary:focus-visible,
.rf-theme .home-screen-hint__close:focus-visible {
    outline: 2px solid var(--rf-glow);
    outline-offset: 3px;
}

/* ══ The Dashboard (templates/my_results_rf.html) ══════════════════════════════════════════ */

.rf-theme .rfd {
    display: flex;
    flex-direction: column;
    gap: 18px;
    width: min(620px, calc(100% - 2.5rem));
    margin: 0 auto;
    padding-top: 16px;
}

.rf-theme .rfd-hello {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 6px 0 0;
}

.rf-theme .rfd-hello h1 {
    font-size: 40px;
    line-height: 1.02;
    overflow-wrap: anywhere;
}

.rf-theme .rfd-progress {
    /* Above the glass that follows it, or the credit explanation opens UNDER the first tile. */
    position: relative;
    z-index: 2;
    display: flex;
    flex-direction: column;
    padding-bottom: 12px;
}

.rf-theme .rfd-progress-head {
    display: flex;
    align-items: center;
    gap: 8px;
    padding-bottom: 16px;
}

.rf-theme .rfd-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--rf-glow);
    box-shadow: 0 0 10px var(--rf-glow);
}

.rf-theme .rfd-level {
    margin-left: auto;
}

.rf-theme .rfd-ring-row {
    display: flex;
    align-items: center;
    gap: 18px;
    padding-bottom: 18px;
}

.rf-theme .rfd-ring-copy {
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 0;
}

.rf-theme .rfd-headline {
    font-size: 23px;
    line-height: 1.1;
}

.rf-theme .rfd-reminder {
    font-size: 12px;
    line-height: 1.4;
}

/* The ONE italic line (Melodie, 2026-09-19): "Day 4 of 7 this week", in the true italic, in
   sentence case, while every other label is upright capitals. */
.rf-theme .rfd-caption {
    margin-top: 2px;
    font-size: 17px;
    font-style: italic;
    font-weight: 500;
    letter-spacing: .01em;
    text-transform: none;
}

.rf-theme .rfd-note {
    margin: 0 0 16px;
    padding: 10px 12px;
    border-left: 3px solid var(--rf-glow);
    border-radius: 0 12px 12px 0;
    background: rgba(255, 255, 255, .06);
    font-size: 13px;
    line-height: 1.4;
}

.rf-theme .rfd-note.is-good {
    border-left-color: #7ED9A0;
}

.rf-theme .rfd-note.is-nudge {
    border-left-color: var(--rf-action);
}

/* The 7-day early unlock's progress line: a goal, so it takes the accent rather than the
   action pink (a nudge) or the green (something already went right). */
.rf-theme .rfd-note.is-unlock {
    border-left-color: var(--rf-graphic);
}

/* ── The comeback day, and the early-unlock celebration ──────────────────────────────────
   Both are a card with an icon, a sentence or two, and at most one button. They use the page's
   own glass card, so a palette change carries them; only the accent stripe names a state. */
.rf-theme .rfd-unlock,
.rf-theme .rfd-comeback {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
    padding: 18px 20px;
    border-left: 3px solid var(--rf-graphic);
}

.rf-theme .rfd-comeback { border-left-color: var(--rf-action); }
.rf-theme .rfd-comeback.is-saved { border-left-color: #7ED9A0; }
.rf-theme .rfd-comeback.is-fresh { border-left-color: var(--rf-graphic); }
/* The health-concerns card (templates/_health_concerns_prompt.html): a tick and two answers. */
.rf-theme .rfd-health { border-left-color: var(--rf-graphic); }
.rf-theme .rfd-health-tick { display: flex; gap: 8px; align-items: center; margin-top: 6px; font-size: 14px; font-weight: 600; color: var(--rf-text); cursor: pointer; }
.rf-theme .rfd-health-tick input { width: 18px; height: 18px; flex: none; accent-color: var(--rf-action); }
.rf-theme .rfd-health-actions { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; }
.rf-theme .rfd-health-remove { appearance: none; background: none; border: 0; padding: 0; font: 600 14px var(--font-sans); color: var(--rf-text-muted); text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }

.rf-theme .rfd-unlock-icon,
.rf-theme .rfd-comeback-icon {
    font-size: 26px;
    line-height: 1;
    flex: none;
}

.rf-theme .rfd-unlock-text,
.rf-theme .rfd-comeback-text {
    display: flex;
    flex-direction: column;
    gap: 4px;
    flex: 1 1 14rem;
    min-width: 0;
}

.rf-theme .rfd-unlock-text strong,
.rf-theme .rfd-comeback-text strong {
    font-size: 15px;
    font-weight: 600;
    line-height: 1.35;
}

.rf-theme .rfd-unlock-text span,
.rf-theme .rfd-comeback-text span {
    font-size: 13px;
    line-height: 1.45;
    color: var(--rf-text-muted);
}

/* The level-up card's "Build a routine" link and its list of the moves the level opened. */
.rf-theme .rfd-unlock-text .rfd-unlock-link {
    color: var(--rf-action);
    font-weight: 600;
    white-space: nowrap;
}
.rf-theme .rfd-unlock-moves { font-size: 13px; line-height: 1.45; }
.rf-theme .rfd-unlock-moves summary {
    cursor: pointer;
    color: var(--rf-action);
    font-weight: 600;
}
.rf-theme .rfd-unlock-moves ul {
    margin: 6px 0 0;
    padding-left: 18px;
    columns: 2 10rem;
    column-gap: 18px;
}

/* A failure has to be legible as a failure, so it is not the muted ink the sentences use. */
.rf-theme .rfd-comeback-text .rfd-comeback-error {
    color: #FF8A80;
    font-weight: 600;
}

.rf-theme .rfd-comeback-btn {
    flex: none;
    min-height: 48px;
    padding: 0 22px;
    font-size: 14px;
}

.rf-theme .rfd-comeback-btn:disabled {
    opacity: .55;
    cursor: default;
}

/* Meet IRIS card on the Dashboard (templates/_dashboard.html; Allison, 2026-09-28). Tokens only, so
   the light Dashboard's palette carries it like every other card here. */
.rf-theme .rfd-iris { display: flex; flex-direction: column; gap: 14px; padding: 18px 20px; border-left: 3px solid var(--rf-action); }
.rf-theme .rfd-iris[hidden] { display: none; }
.rf-theme .rfd-iris-head { display: flex; align-items: center; gap: 14px; }
.rf-theme .rfd-iris-img { width: 88px; height: 88px; object-fit: contain; flex: none; }
.rf-theme .rfd-iris-text { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.rf-theme .rfd-iris-text strong { font-size: 17px; font-weight: 600; line-height: 1.3; }
.rf-theme .rfd-iris-text span { font-size: 14px; line-height: 1.45; color: var(--rf-text-muted); }
.rf-theme .rfd-iris-cd .beta-cd { max-width: none; padding: 10px 12px; background: transparent; border: 1px solid var(--rf-hairline, rgba(252, 60, 173, 0.3)); color: inherit; }
.rf-theme .rfd-iris-cd .beta-cd__title { color: var(--rf-text-muted); letter-spacing: 0.08em; }
.rf-theme .rfd-iris-cd .beta-cd__num { color: inherit; font-size: 1.5rem; }
.rf-theme .rfd-iris-cd .beta-cd__label { color: var(--rf-text-muted); }
.rf-theme .rfd-iris-btn { min-height: 48px; }

.rf-theme .rfd-stats {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr);
    gap: 12px;
    padding: 16px 0 6px;
}

.rf-theme .rfd-stat {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.rf-theme .rfd-stat--freeze {
    padding-left: 14px;
    border-left: 1px solid rgba(255, 255, 255, .12);
}

.rf-theme .rfd-stat-value {
    font-size: 20px;
}

.rf-theme .rfd-freeze {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12px;
    line-height: 1.35;
}

/* A link that lands on a section (Settings' quick links) leaves a little air above it. */
.rf-theme .rfd-section[id],
.rf-theme .rf-fold[id] {
    scroll-margin-top: 16px;
}

.rf-theme .rfd-section {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding-top: 8px;
}

/* NOT a flex row. A label's larger first letters are separate spans, and a flex container spaces
   out every child it has, so "My Saved Routines" came out as "M y  S aved  R outines". The count
   chip sits inline after the words instead. No element that carries .rf-eyebrow may be a flex or
   grid container (test_refaced_dashboard.py holds every such rule to that). */
.rf-theme .rfd-section-head .rf-chip {
    display: inline-block;
    margin-left: 8px;
    vertical-align: middle;
}

/* Three across at every width, as the classic page had them: 2-Minute Miracle, the 9-Minute
   Method, and the slot Melodie fills when she features a routine. */
.rf-theme .rfd-sigs {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 10px;
}

.rf-theme .rfd-sigs-learn {
    margin: 10px 0 0;
    font-size: 13px;
    text-align: center;
}

.rf-theme .rfd-sigs-learn a {
    color: inherit;
    text-decoration: underline;
    text-underline-offset: 2px;
}

.rf-theme .rfd-rows {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.rf-theme .rfd-slots {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 0;
    font-size: 12px;
}

.rf-theme .rfd-slots[hidden] {
    display: none;
}

.rf-theme .rfd-build-link {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    margin-left: auto;
    color: var(--rf-glow);
    font-weight: 700;
}

.rf-theme .rfd-empty {
    margin: 0;
    padding: 16px 18px;
    border-radius: 20px;
    font-size: 14px;
    line-height: 1.5;
}

/* ── Classic Dashboard pieces, re-inked for the dark page. Their markup, classes and scripts are
   the classic ones on purpose (one copy of the behaviour); only the paint is set here, because
   the classic paint lives in my_results.html's own <style>, which this page does not load. ── */
.rf-theme .home-screen-hint[hidden] {
    display: none !important;
}

.rf-theme .home-screen-hint {
    position: relative;
    padding: 12px 52px 12px 16px;
    border-radius: 18px;
    font-size: 14px;
    line-height: 1.5;
}

.rf-theme .home-screen-hint__lead {
    margin: 0;
}

.rf-theme .home-screen-hint summary {
    display: flex;
    align-items: center;
    min-height: 44px;
    color: var(--rf-glow);
    font-weight: 700;
    text-decoration: underline;
    cursor: pointer;
}

.rf-theme .home-screen-hint__steps {
    display: grid;
    gap: 10px;
    margin: 4px 0 0;
    padding-left: 18px;
}

.rf-theme .home-screen-hint__steps em {
    font-style: normal;
    font-weight: 600;
}

.rf-theme .home-screen-hint__note {
    margin: 10px 0 0;
    color: var(--rf-text-muted);
}

.rf-theme .home-screen-hint__close {
    position: absolute;
    top: 4px;
    right: 4px;
    min-width: 44px;
    min-height: 44px;
    border: 0;
    background: none;
    color: var(--rf-text);
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
}

.rf-theme .activity-grid-wrap {
    display: flex;
    gap: 8px;
    overflow-x: auto;
    padding-bottom: 4px;
}

/* Mon to Sun down the left edge, one label per calendar row: seven equal rows under the space
   the month names take (14px label + 4px gap), so each label lines up with its row of squares. */
.rf-theme .activity-day-labels {
    display: grid;
    grid-template-rows: repeat(7, 1fr);
    gap: 4px;
    flex: none;
    margin-top: 18px;
    /* The grid scrolls sideways on a small phone and opens on the newest week; the day names stay
       pinned to the left edge so the rows never lose their labels. */
    position: sticky;
    left: 0;
    z-index: 1;
    padding-right: 6px;
    /* No solid patch behind them: the card is see-through glass over a background that changes
       colour, so any one colour shows as a slab. Squares sliding under the letters are dimmed by
       the blur instead, and the letters carry a soft shadow to stay readable over a gold square. */
    -webkit-backdrop-filter: blur(6px) brightness(.55);
    backdrop-filter: blur(6px) brightness(.55);
    text-shadow: 0 1px 2px rgba(23, 13, 25, .9);
    border-radius: 6px;
    font: 600 11px var(--font-sans);
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--rf-text-muted);
}

.rf-theme .activity-day-labels span {
    display: flex;
    align-items: center;
}

.rf-theme .activity-grid {
    display: flex;
    gap: 4px;
    flex: 1;
}

/* 20px squares + 4px gaps = a 24px pitch, the smallest target WCAG 2.5.8 accepts. Thirteen of
   them do not fit a small phone, so the grid scrolls sideways there and the page opens it on the
   newest week (the script in my_results_rf.html). */
.rf-theme .activity-col {
    display: flex;
    flex-direction: column;
    gap: 4px;
    flex: 1;
    min-width: 20px;
}

.rf-theme .activity-month-label {
    height: 14px;
    font: 600 11px var(--font-sans);
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--rf-text-muted);
    white-space: nowrap;
}

.rf-theme .activity-cell {
    width: 100%;
    aspect-ratio: 1;
    border-radius: 4px;
}

.rf-theme .activity-cell--missed {
    background: rgba(255, 255, 255, .09);
}

/* A missed day a streak freeze covered: part of the run, and visibly not a practice. The
   Refaced calendar predates the bank and drew it as a plain miss. */
.rf-theme .activity-cell--frozen,
.rf-theme .legend-cell--frozen {
    background: color-mix(in srgb, var(--rf-graphic) 45%, transparent);
}

.rf-theme .activity-cell--practiced {
    background: var(--rf-glow);
}

.rf-theme .activity-cell--future {
    border: 1px dashed rgba(255, 255, 255, .16);
}

.rf-theme .activity-cell--today {
    outline: 2px solid var(--rf-text);
    outline-offset: 1px;
}

.rf-theme .activity-cell[data-date] {
    cursor: pointer;
}

.rf-theme .activity-legend {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 10px;
}

.rf-theme .legend-cell {
    width: 10px;
    height: 10px;
    margin-left: 10px;
    border-radius: 3px;
}

.rf-theme .legend-cell:first-child {
    margin-left: 0;
}

.rf-theme .legend-cell--missed {
    background: rgba(255, 255, 255, .09);
}

.rf-theme .legend-cell--practiced {
    background: var(--rf-glow);
}

.rf-theme .legend-label {
    font-size: 11px;
    color: var(--rf-text-muted);
}

.rf-theme .day-detail {
    position: relative;
    margin-top: 14px;
    padding: 12px 48px 12px 14px;
    border: 1px solid var(--rf-glass-border);
    border-radius: 14px;
    background: rgba(255, 255, 255, .06);
}

.rf-theme .day-detail[hidden] {
    display: none;
}

.rf-theme .day-detail-date,
.rf-theme .practice-log-date {
    font: 700 13px var(--font-sans);
    color: var(--rf-text);
}

.rf-theme .day-detail-date {
    margin-bottom: 8px;
}

.rf-theme .day-detail-routines,
.rf-theme .practice-log-routines {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.rf-theme .day-detail-routines li,
.rf-theme .practice-log-routines li {
    padding: 3px 10px;
    border-radius: 999px;
    background: rgba(255, 255, 255, .1);
    color: var(--rf-text);
    font-size: 12px;
}

.rf-theme .day-detail-close {
    position: absolute;
    top: 0;
    right: 0;
    width: 44px;
    height: 44px;
    border: 0;
    background: none;
    color: var(--rf-text);
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
}

.rf-theme .practice-log {
    margin-top: 18px;
}

.rf-theme .practice-log-title {
    margin: 0 0 8px;
}

.rf-theme .practice-log-day {
    display: flex;
    gap: 14px;
    padding: 9px 0;
    border-top: 1px solid rgba(255, 255, 255, .08);
}

.rf-theme .practice-log-date {
    flex: 0 0 8.5rem;
}

.rf-theme .practice-log-more {
    margin: 10px 0 0;
    font-size: 12px;
}

@media (max-width: 560px) {
    .rf-theme .practice-log-day { flex-direction: column; gap: 6px; }
    .rf-theme .practice-log-date { flex-basis: auto; }
    .rf-theme .rfd-ring-row { gap: 14px; }
    .rf-theme .rf-loop { width: 124px; height: 124px; }
}

/* The smallest phones (320px): the ring gets its own line instead of crushing its words into a
   100px column, and the three signature tiles tighten up so their words still fit whole. */
@media (max-width: 359px) {
    .rf-theme .rfd-ring-row { flex-direction: column; align-items: flex-start; }
    .rf-theme .rfd-sigs { gap: 6px; }
    .rf-theme .rf-tile .rf-pill { padding: 6px 6px; font-size: 11px; }
    .rf-theme .rf-tile-name { font-size: 13px; }
    .rf-theme .rf-tile-meta { letter-spacing: .04em; }
}

/* Rename / delete: the classic white modal, as a dark card with the page's light text. */
.rf-theme .save-modal {
    border: 1px solid var(--rf-glass-border);
    background: var(--rf-panel-bg);
    color: var(--rf-text);
}

.rf-theme .save-modal h2 {
    margin: 0 0 12px;
    font: 600 26px var(--font-display);
    color: var(--rf-text);
}

.rf-theme .modal-body {
    margin: 0 0 4px;
    font-weight: 600;
}

.rf-theme .modal-count {
    margin: 0 0 20px;
    color: var(--rf-text-muted);
    font-size: 13px;
}

.rf-theme .modal-error {
    margin: 0 0 12px;
    color: #FF8A80;
    font-size: 13px;
}

.rf-theme .modal-close {
    position: absolute;
    top: 6px;
    right: 6px;
    width: 44px;
    height: 44px;
    border: 0;
    background: none;
    color: var(--rf-text);
    font-size: 18px;
    cursor: pointer;
}

.rf-theme .rename-input {
    width: 100%;
    margin: 0 0 10px;
    padding: 12px 14px;
    border: 1px solid rgba(244, 236, 241, .55);
    border-radius: 12px;
    background: rgba(255, 255, 255, .08);
    color: var(--rf-text);
    font: 500 16px var(--font-sans);
}

.rf-theme .save-modal .btn {
    min-height: 48px;
    border-radius: 999px;
    font: 700 15px var(--font-sans);
    cursor: pointer;
}

.rf-theme .save-modal .btn-primary {
    border: 0;
    background: var(--rf-action);
    color: #fff;
}

.rf-theme .save-modal .btn-delete {
    border: 1px solid #FF8A80;
    background: transparent;
    color: #FF8A80;
}

.rf-theme .save-modal .btn-ghost {
    border: 1px solid var(--rf-glass-border);
    background: transparent;
    color: var(--rf-text);
}

.rf-theme .save-modal .btn:focus-visible,
.rf-theme .rename-input:focus-visible,
.rf-theme .modal-close:focus-visible {
    outline: 2px solid var(--rf-glow);
    outline-offset: 3px;
}

/* First-routine player tour (style.css has the layout): the spotlight ring in the Refaced gold. */
.rf-theme .tour-spotlight {
    box-shadow: 0 0 0 9999px rgba(10, 6, 14, 0.62), 0 0 0 2px var(--rf-glow) inset;
}

/* ══ Progress photos on the Dashboard (pass B) ═════════════════════════════════════════════
   The markup, wording and script are the photos page's own (templates/_photo_intro.html,
   _photo_tool_body.html, static/js/photo_uploader.js). Only the paint is set here: the classic
   paint is written for a white card. Opacity is never used to mute these words; on glass it
   multiplies against whatever is behind the card and cannot be measured. */
.rf-theme .rfd-photo-intro {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding-bottom: 14px;
}

.rf-theme .rfd-photo-intro .account-email {
    margin: 0;
    opacity: 1 !important;
    font-size: 13px !important;
    line-height: 1.5;
    color: var(--rf-text-muted);
}

.rf-theme .rfd-photo-tool a {
    color: var(--rf-glow);
}

.rf-theme .rfd-photo-tool .consent-row {
    margin-bottom: 12px;
}

.rf-theme .rfd-photo-tool .consent-row input[type="checkbox"] {
    width: 22px;
    height: 22px;
    margin-top: 1px;
    accent-color: var(--rf-action);
}

.rf-theme .rfd-photo-tool .consent-row span {
    opacity: 1;
    font-size: 13px;
    line-height: 1.55;
    color: var(--rf-text);
}

.rf-theme .rfd-photo-tool .consent-withdraw-note {
    margin: 0 0 12px 34px;
    font-size: 13px;
    line-height: 1.5;
    color: var(--rf-text-muted);
}

.rf-theme .rfd-photo-tool .photo-dropzone {
    margin-top: 4px;
    border-color: rgba(244, 236, 241, .4);
    color: var(--rf-text);
}

.rf-theme .rfd-photo-tool .photo-dropzone.is-dragover {
    border-color: var(--rf-glow);
    background: rgba(226, 191, 107, .08);
}

.rf-theme .rfd-photo-tool .photo-dropzone-inner small {
    opacity: 1;
    font-size: 12px;
    color: var(--rf-text-muted);
}

.rf-theme .rfd-photo-tool .link-button {
    min-height: 44px;
    color: var(--rf-glow);
    font-weight: 700;
}

.rf-theme .rfd-photo-tool .photo-error {
    color: #FF8A80;
}

.rf-theme .rfd-photo-tool .photo-error.is-note {
    color: var(--rf-text);
}

.rf-theme .rfd-photo-tool .photo-ephemeral-note {
    color: var(--rf-text-muted);
    border-color: var(--rf-glass-border);
}

.rf-theme .rfd-photo-tool .photo-thumb.is-compare-selected {
    box-shadow: 0 0 0 3px var(--rf-glow);
}

.rf-theme .rfd-photo-tool .photo-compare {
    margin-top: 20px;
    padding-top: 18px;
    border-top-color: rgba(255, 255, 255, .1);
}

.rf-theme .rfd-photo-tool .photo-compare-heading {
    margin: 0 0 4px;
    font: 600 21px var(--font-display);
    color: var(--rf-text);
}

.rf-theme .rfd-photo-tool .photo-compare-hint {
    margin: 0 0 14px;
    font-size: 13px;
    color: var(--rf-text-muted);
}

/* Side by side at EVERY width (Melodie, 2026-09-18). The classic page stacks the two photos and
   turns the swap button on its side under 480px, while its own words say "side by side". */
.rf-theme .rfd-photo-tool .photo-compare-slots {
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    gap: 10px;
}

.rf-theme .rfd-photo-tool .photo-compare-swap {
    justify-self: auto;
    transform: none;
    width: 44px;
    height: 44px;
    border: 1px solid rgba(255, 255, 255, .3);
    background: rgba(255, 255, 255, .08);
    color: var(--rf-text);
}

.rf-theme .rfd-photo-tool .photo-compare-slot {
    border-color: rgba(244, 236, 241, .4);
}

.rf-theme .rfd-photo-tool .photo-compare-slot.is-dragover,
.rf-theme .rfd-photo-tool .photo-compare-slot.is-filled {
    border-color: var(--rf-glow);
}

.rf-theme .rfd-photo-tool .photo-compare-slot-placeholder {
    font: 600 12px var(--rf-font-label);
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--rf-text-muted);
}

.rf-theme .rfd-photo-tool .link-button:focus-visible,
.rf-theme .rfd-photo-tool .photo-compare-swap:focus-visible,
.rf-theme .rfd-photo-tool .photo-thumb:focus-visible,
.rf-theme .rfd-photo-tool button:focus-visible,
.rf-theme .rfd-photo-tool input:focus-visible,
.rf-theme .rfd-photo-tool a:focus-visible {
    outline: 2px solid var(--rf-glow);
    outline-offset: 3px;
}

/* Delete, share and clear sit ON a photo and stay the size the classic page draws them, but a
   thumb needs 44px to hit them: two of the three delete or publish a photo of her face. The hit
   area grows, the drawn button does not. */
.rf-theme .rfd-photo-tool .photo-thumb-delete::before,
.rf-theme .rfd-photo-tool .photo-compare-slot-clear::before {
    content: "";
    position: absolute;
    inset: -9px;
}

.rf-theme .rfd-photo-tool .photo-thumb-share {
    min-height: 32px;
}

.rf-theme .rfd-photo-tool .photo-thumb-share::before {
    content: "";
    position: absolute;
    inset: -6px 0;
}

/* ══ Settings (templates/account.html) ═════════════════════════════════════════════════════
   ONE template serves both looks: the page is mostly consent and legal wording (email
   preferences, study consent, data download, account deletion), and that must not exist twice.
   So nothing here touches its words or its forms; this only repaints its classes, each of which
   is declared in the page's own <style> for a white page. .rf-theme .x (two classes) out-ranks
   the page's .x whatever the source order. */
.rf-theme .settings-page {
    width: min(620px, calc(100% - 2.5rem));
    max-width: none;
    padding: 16px 0 0;
}

.rf-theme .settings-header {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-bottom: 18px;
}

.rf-theme .settings-header h1 {
    margin: 0;
    font: 600 40px/1.02 var(--font-display);
    letter-spacing: -.01em;
    color: var(--rf-text);
    overflow-wrap: anywhere;
}

.rf-theme .settings-email,
.rf-theme .settings-hint,
.rf-theme .settings-meta,
.rf-theme .settings-study-note {
    color: var(--rf-text-muted);
}

.rf-theme .settings-section {
    margin-bottom: 16px;
    padding: 20px;
    border: 1px solid var(--rf-glass-border);
    border-radius: 24px;
    background: var(--rf-glass-bg);
    -webkit-backdrop-filter: blur(20px) saturate(150%);
    backdrop-filter: blur(20px) saturate(150%);
    box-shadow: var(--rf-glass-shadow);
}

.rf-theme .settings-section--last {
    margin-bottom: 0;
}

.rf-theme .settings-heading {
    margin: 0 0 14px;
    padding-bottom: 10px;
    border-bottom-color: rgba(255, 255, 255, .1);
    font: 600 23px/1.1 var(--font-display);
    color: var(--rf-text);
}

.rf-theme .settings-page a {
    color: var(--rf-glow);
}

.rf-theme .settings-status--active {
    background: rgba(126, 217, 160, .14);
    color: #7ED9A0;
}

.rf-theme .settings-status--warn {
    background: rgba(226, 191, 107, .16);
    color: var(--rf-glow);
}

.rf-theme .settings-status--inactive {
    background: rgba(255, 255, 255, .1);
    color: var(--rf-text);
}

.rf-theme .settings-level-line {
    margin: 0;
    font-size: 15px;
    line-height: 1.5;
    color: var(--rf-text);
}

.rf-theme .settings-list-row {
    border-color: var(--rf-glass-border);
}

.rf-theme .settings-list-name {
    color: var(--rf-text);
}

.rf-theme .settings-pill--live {
    color: #7ED9A0;
}

.rf-theme .settings-pill--lapsed {
    color: var(--rf-glow);
}

.rf-theme .settings-page .form-label {
    opacity: 1;
    color: var(--rf-text);
}

.rf-theme .settings-page .form-label span {
    opacity: 1 !important;
    color: var(--rf-text-muted);
}

.rf-theme .settings-page .form-input {
    border: 1px solid rgba(244, 236, 241, .55);
    background: rgba(255, 255, 255, .08);
    color: var(--rf-text);
}

.rf-theme .settings-page .form-input:focus {
    border-color: var(--rf-glow);
    background: rgba(255, 255, 255, .1);
}

.rf-theme .settings-page .form-input::placeholder {
    color: rgba(244, 236, 241, .5);
}

/* Her email is shown but cannot be edited here. The page dims it with an inline opacity, which
   on glass cannot be measured; it is muted with the text token instead. */
.rf-theme .settings-page .form-input:disabled {
    opacity: 1 !important;
    border-style: dashed;
    color: var(--rf-text-muted);
}

.rf-theme .settings-consent-row input[type="checkbox"],
.rf-theme .settings-page .consent-row input[type="checkbox"] {
    width: 22px;
    height: 22px;
    accent-color: var(--rf-action);
}

.rf-theme .settings-consent-row span,
.rf-theme .settings-page .consent-row span {
    opacity: 1;
    font-size: 15px;
    color: var(--rf-text);
}

.rf-theme .settings-links a {
    min-height: 44px;
    color: var(--rf-text);
}

.rf-theme .settings-links a span {
    color: var(--rf-glow);
}

.rf-theme .settings-links a:hover {
    color: var(--rf-glow);
}

.rf-theme .settings-page .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    padding: 0 22px;
    border-radius: 999px;
    font: 700 15px var(--font-sans);
    text-decoration: none;
    cursor: pointer;
    box-shadow: none;
}

.rf-theme .settings-page .btn-primary {
    border: 0;
    background: var(--rf-action);
    color: #fff;
}

.rf-theme .settings-page .btn-secondary {
    border: 1px solid rgba(244, 236, 241, .55);
    background: transparent;
    color: var(--rf-text);
}

/* Lighter than the #FF8A80 used elsewhere: this button sits on the delete section's red tint,
   and over an atmosphere shape #FF8A80 measured 4.36:1 there. */
.rf-theme .settings-page .btn-delete {
    border: 1px solid #FFA8A1;
    background: transparent;
    color: #FFA8A1;
}

.rf-theme .settings-page .req-star {
    color: #FFA8A1;
}

.rf-theme .settings-page .consent-row {
    margin-bottom: 12px;
}

.rf-theme .settings-danger {
    border-top-color: rgba(255, 138, 128, .3);
}

.rf-theme .settings-danger-toggle {
    min-height: 44px;
    color: #FF8A80;
}

.rf-theme .settings-danger-body {
    border-color: rgba(255, 138, 128, .3);
    background: rgba(255, 138, 128, .06);
}

.rf-theme .settings-danger-body strong {
    color: var(--rf-text);
}

.rf-theme .settings-page a:focus-visible,
.rf-theme .settings-page button:focus-visible,
.rf-theme .settings-page input:focus-visible,
.rf-theme .settings-page summary:focus-visible {
    outline: 2px solid var(--rf-glow);
    outline-offset: 3px;
}

/* ══ The routine player (step 4): templates/results.html, both `results` and `saved_routine` ══
   Melodie, 2026-09-18: NOTHING that is on screen while a routine plays changes colour: the green
   progress bar and play button, the pink move banner, the reminder, the buffering screen, the
   tap-to-begin card all keep their classic rules in style.css. The theme may name one of them
   for ONE reason only: to hand it back the exact classic value a general theme rule took away
   (test_refaced_player.py holds every such value equal to style.css). What IS repainted is what she
   sees before and after: the loading shimmer, the finished card's label and buttons, the save
   pop-up's name box, and the routine note under the player. The player's script and the stage's
   geometry are not touched. */

/* HANDED BACK, not repainted. `.rf-theme p { color: inherit }` (top of this file) out-ranks any
   classic paragraph that gets its colour from a single class. Found by comparing every in-play
   element's computed style in the two looks: the instruction line shown DURING play had gone
   from the classic pure white to the theme's off-white, and the finished card's sentence from
   78% white to solid. Each gets its classic value back, word for word. */
.rf-theme .clip-instruction {
    color: #ffffff;
}

.rf-theme .finished-card p {
    color: rgba(255, 255, 255, 0.78);
}

/* No floating tab bar on this page (config.REFACED_NO_TABBAR_ENDPOINTS): the move banner and the
   pause controls sit at the bottom of the video, where the bar floats on a phone. With no bar,
   the four things that keep clear of it go back to where the classic player has them today.
   NOT done by redefining --rf-tabbar-clearance: no token may be defined twice in this file (a
   later definition silently wins over the checked one), and that guard is worth more than the
   four rules it costs here. */
.rf-theme.rf-no-tabbar .site-footer {
    padding-bottom: 2.5rem;
}

.rf-theme.rf-no-tabbar .ftfl-toast {
    bottom: 1.5rem;
}

.rf-theme.rf-no-tabbar .beta-fab-wrap {
    bottom: calc(16px + env(safe-area-inset-bottom, 0px));
}

/* Melodie, 2026-09-19: on the player the banner sits FLUSH with the bottom edge, full width, as
   the classic one does. As a rounded card inset 12px it was narrower, so its words wrapped
   taller, and lifted 10px: 31px higher on a phone, enough to cover the pause controls until she
   answered it. The four values are style.css's own (test_refaced_player_browser.py holds the
   banner's box equal to classic's). Its white card and dark ink are untouched. */
.rf-theme.rf-no-tabbar .consent-banner {
    left: 0;
    right: 0;
    bottom: 0;
    border-radius: 0;
}

/* iPhone Safari's bottom toolbar covers a button pinned at 16px; the classic page lifts it to
   72px on a phone for that reason, and so does this one. */
@media (max-width: 520px) {
    .rf-theme.rf-no-tabbar .beta-fab-wrap {
        bottom: calc(72px + env(safe-area-inset-bottom, 0px));
    }
}

/* The note sits on the page, not on the video: classic dark ink would be dark on dark. Text
   tokens only, never opacity (it cannot be measured on the atmosphere), and upright: the one
   italic line in the new look is the Dashboard ring's caption. */
.rf-theme .routine-note {
    color: var(--rf-text);
    opacity: 1;
    font-style: normal;
}

/* The duty-of-care line is quieter than the note but must be read. The muted token is a checked
   colour (7:1 on the page), not a dimmed one. */
.rf-theme .routine-note-reassurance {
    color: var(--rf-text-muted);
    opacity: 1;
}

/* Waiting for the first clips: the old pink-to-green square becomes a gold ring that turns,
   the same gold loop the Dashboard uses for progress. */
.rf-theme .loading-shimmer {
    border: 3px solid rgba(226, 191, 107, .28);
    border-top-color: var(--rf-glow);
    border-radius: 50%;
    background: transparent;
    box-shadow: 0 0 18px -4px var(--rf-glow);
    animation: rfLoopTurn 1.1s linear infinite;
}

@keyframes rfLoopTurn {
    to { transform: rotate(360deg); }
}

/* She asked for less motion: the ring holds still and the words carry the wait. */
@media (prefers-reduced-motion: reduce) {
    .rf-theme .loading-shimmer {
        animation: none;
    }
}

/* The finished card: its label is a Refaced label (markup from rf_label), its buttons the one
   pink and a quiet outline. The card itself, over the last frame of video, keeps its dark ink. */
.rf-theme .finished-card .rf-eyebrow {
    display: block;
}

.rf-theme .finished-actions .btn {
    min-height: 52px;
    border-radius: 999px;
    font: 700 16px var(--font-sans);
    cursor: pointer;
}

.rf-theme .finished-actions .btn-primary {
    border: 0;
    background: var(--rf-action);
    color: #fff;
    box-shadow: 0 16px 36px -14px var(--rf-action);
}

/* "Saved" after Save for later: a done state, so it must not look pressable. */
.rf-theme .finished-actions .btn:disabled {
    opacity: .6;
    box-shadow: none;
    cursor: default;
}

.rf-theme .finished-actions .btn-secondary {
    border: 1px solid rgba(244, 236, 241, .55);
    background: transparent;
    color: var(--rf-text);
    box-shadow: none;
}

.rf-theme .finished-actions .btn:focus-visible {
    outline: 2px solid var(--rf-glow);
    outline-offset: 3px;
}

/* The save pop-up is already Refaced (the Dashboard's modals share .save-modal); this page's
   name box and its label use their own classes, written for a white card. */
.rf-theme .modal-name-row label {
    color: var(--rf-text);
}

.rf-theme .save-modal .form-input {
    border: 1px solid rgba(244, 236, 241, .55);
    border-radius: 12px;
    background: rgba(255, 255, 255, .08);
    color: var(--rf-text);
    font: 500 16px var(--font-sans);
}

.rf-theme .save-modal .form-input::placeholder {
    color: var(--rf-text-muted);
}

.rf-theme .save-modal .form-input:focus-visible {
    outline: 2px solid var(--rf-glow);
    outline-offset: 3px;
}

/* ══ The LIGHT palette ═══════════════════════════════════════════════════════════════════════

   Melodie, 2026-09-22: the Dashboard members see today is to work exactly like the Refaced one
   and keep its light colours. That is this block. It is a palette, not a second design: the
   markup is the same file (_dashboard.html) and every rule below only re-states a colour the
   dark theme states in white-on-dark terms.

   `.rf-light` never goes on <body>. It goes on a DIV inside an otherwise classic page, so the
   classic header, footer and cookie banner are outside it and keep their own rules.

   HER PALETTE, and the one place I did not use it literally: she chose #6c63ff. As lettering on
   this page it measures 4.13:1, under the 4.5:1 floor this app has held since the 2026-07-22
   contrast sweep, so the WORDS take #5B52E0 (5.40:1) and the SHAPES keep #6c63ff, where 3:1 is
   the bar and it is comfortably over it. The two are a hair apart on screen.
   test_refaced_light.py does this arithmetic; change a value here and it has to agree. */

.rf-theme.rf-light {
    --rf-base: #F8F8FB;                        /* what the classic page's gradient averages to */
    --rf-blob: #F3F0FF;
    --rf-glow: #5B52E0;                        /* the accent as INK */
    --rf-graphic: #6C63FF;                     /* the accent as a SHAPE: her purple, untouched */
    --rf-action: #D81F8F;
    --rf-text: #101018;
    --rf-text-muted: rgba(16, 16, 24, .66);
    --rf-glass-bg: #FFFFFF;
    --rf-glass-border: rgba(16, 16, 24, .08);
    --rf-glass-shadow: 0 2px 16px rgba(0, 0, 0, .07);
    --rf-panel-bg: #FFFFFF;
    --primary-color: #D81F8F;
    --primary-strong: #B8177A;

    /* The classic page already has its own soft gradient; painting a flat colour over it would
       leave a visible rectangle where this block starts. */
    background: transparent;
    color: var(--rf-text);
    color-scheme: light;
}

/* A veil of white over a dark surface is the dark theme's way of lifting one layer off another.
   On a light page the veil has to go the other way, or it is invisible. Each rule below is the
   light twin of a dark one; test_refaced_light.py fails when a dark rule gains a white veil and
   no twin appears here. */
.rf-theme.rf-light .rf-hr,
.rf-theme.rf-light .rf-chip { background: rgba(16, 16, 24, .07); }
.rf-theme.rf-light .rf-chip { color: var(--rf-glow); }
.rf-theme.rf-light .rf-pill.is-soon { background: rgba(16, 16, 24, .06); color: rgba(16, 16, 24, .66); }
.rf-theme.rf-light .rf-shield { stroke: rgba(16, 16, 24, .3); }
.rf-theme.rf-light .rf-shield.is-banked { fill: var(--rf-graphic); }
.rf-theme.rf-light .rf-loop-track { stroke: rgba(16, 16, 24, .1); }
.rf-theme.rf-light .rf-tile-art { background: rgba(16, 16, 24, .06); }
.rf-theme.rf-light .rf-row-thumb { background: rgba(16, 16, 24, .06); }
.rf-theme.rf-light .rfd-note { background: rgba(108, 99, 255, .07); }
.rf-theme.rf-light .rfd-stat--freeze { border-left-color: rgba(16, 16, 24, .1); }
/* White lettering with a dark shadow becomes dark lettering with no shadow. */
.rf-theme.rf-light .activity-day-labels { text-shadow: none; }
.rf-theme.rf-light .activity-cell--missed,
.rf-theme.rf-light .legend-cell--missed { background: rgba(16, 16, 24, .08); }
.rf-theme.rf-light .activity-cell--future { border-color: rgba(16, 16, 24, .14); }
.rf-theme.rf-light .day-detail { background: rgba(108, 99, 255, .06); }
.rf-theme.rf-light .day-detail-routines li,
.rf-theme.rf-light .practice-log-routines li { background: rgba(216, 31, 143, .08); color: var(--primary-strong); }
.rf-theme.rf-light .practice-log-day { border-top-color: rgba(16, 16, 24, .07); }
.rf-theme.rf-light .rename-input,
.rf-theme.rf-light .save-modal .form-input {
    border-color: rgba(16, 16, 24, .18);
    background: #FFFFFF;
}
/* #C0271D is 5.9:1 on white; the dark theme's #FF8A80 is 1.6:1 there. */
.rf-theme.rf-light .modal-error,
.rf-theme.rf-light .photo-error,
.rf-theme.rf-light .save-modal .btn-delete { color: #C0271D; }
.rf-theme.rf-light .save-modal .btn-delete { border-color: #C0271D; }
.rf-theme.rf-light .rfd-photo-tool .photo-dropzone,
.rf-theme.rf-light .rfd-photo-tool .photo-compare-slot { border-color: rgba(16, 16, 24, .22); }
.rf-theme.rf-light .rfd-photo-tool .photo-dropzone.is-dragover { background: rgba(108, 99, 255, .08); }
.rf-theme.rf-light .rfd-photo-tool .photo-compare { border-top-color: rgba(16, 16, 24, .1); }
/* #2F855A is 4.6:1 on white; the dark theme's #7ED9A0 is 1.8:1 there. */
.rf-theme.rf-light .rfd-comeback.is-saved { border-left-color: #2F855A; }
.rf-theme.rf-light .rfd-note.is-good { border-left-color: #2F855A; }
.rf-theme.rf-light .rfd-comeback-text .rfd-comeback-error { color: #C0271D; }

.rf-theme.rf-light .rfd-photo-tool .photo-compare-swap {
    border-color: rgba(16, 16, 24, .22);
    background: rgba(16, 16, 24, .04);
}


/* ══ The lesson / course player (step 5): templates/lesson_player.html, endpoint `lesson_player` ══
   An ORDINARY SCROLLING PAGE, not a stage: one video with the browser's own controls, a complete
   button, previous/next, a celebrate banner with its own confetti, the lesson's text from the
   database, and sometimes a "Your answers" form. Melodie, 2026-09-19: nothing on this page has to
   keep its classic look, and it shows NO floating tab bar (`config.REFACED_NO_TABBAR_ENDPOINTS`),
   for the reason the routine player shows none: the classic 70vh cap puts the video's own control
   strip in the band a floating bar sits in. She was first told the opposite, on a premise that
   turned out to be wrong; see the build log, review rounds 1 and 2. Every rule below hangs off `.lesson-page`, this page's own hook: the wrapper
   also carries `player-page`, which the ROUTINE player uses, and that page is already pinned. */

.rf-theme .lesson-page .player-crumbs a {
    color: var(--rf-glow);
}

.rf-theme .lesson-page .player-progress-mini {
    color: var(--rf-text-muted);
}

.rf-theme .lesson-page .player-title {
    color: var(--rf-text);
}

/* The module's name above the lesson's. Classic pinks it; on this base the theme's
   `--primary-strong` would be #B5126C, which is 2.4:1 here. Gold is the page's own accent. */
.rf-theme .lesson-page .player-module {
    color: var(--rf-glow);
}

/* SETTLED 2026-09-19: this page shows no floating tab bar, so the video keeps the classic 70vh
   cap and nothing of the theme's sits on its controls. A cap of
   `calc(70vh - var(--rf-tabbar-clearance))` was tried while the bar was still here and REVERTED:
   two independent reviewers measured that it only moved the overlap onto "Mark lesson complete",
   still failed on a landscape phone and at 320x568, and cost 74px of video at every size. There
   was no arithmetic to be had, because the room above the video is a header, a breadcrumb and a
   title that wraps. See docs/BUILDLOG-refaced-theme.md, review rounds 1 and 2. */
.rf-theme .lesson-page .player-media video,
.rf-theme .lesson-page .player-media audio {
    border-radius: 16px;
}

.rf-theme .lesson-page .player-actions .btn {
    box-shadow: none;
}

.rf-theme .lesson-page .player-complete-btn {
    background: var(--rf-action);
    color: #fff;
    border: 0;
}

/* Already finished: the button is disabled, and a solid pink that cannot be pressed reads as an
   action. A quiet glass tick, with its words at full strength (the classic page dims the whole
   button with the browser's default disabled rendering). */
.rf-theme .lesson-page .player-complete-btn:disabled {
    background: var(--rf-glass-bg);
    border: 1px solid var(--rf-glass-border);
    color: var(--rf-text);
    opacity: 1;
}

.rf-theme .lesson-page .player-nav-row .btn-secondary {
    background: transparent;
    border: 1px solid rgba(244, 236, 241, .55);
    color: var(--rf-text);
}

.rf-theme .lesson-page .player-actions .btn:focus-visible {
    outline: 2px solid var(--rf-glow);
    outline-offset: 3px;
}

/* The breadcrumb back to the course is an ordinary link, and `style.css` gives it no ring of its
   own: it was left with the browser's default, which `color-scheme: dark` repaints. A link
   inside Allison's TEXT is deliberately not included: it sits on the card, which is the classic
   surface, and it keeps exactly the ring the classic page draws. */
.rf-theme .lesson-page .player-crumbs a:focus-visible {
    outline: 2px solid var(--rf-glow);
    outline-offset: 3px;
    border-radius: 4px;
}

/* This page has NO floating tab bar (config.REFACED_NO_TABBAR_ENDPOINTS, settled with Melodie on
   2026-09-19; see the build log, review round 2). So the four rules this step first carried
   because of the bar are GONE, not kept as harmless improvements: the page's extra bottom
   padding, the lifts that put the celebrate banner, its confetti and the routine toast above
   z-index 800, and the toast's clearance. All four now sit exactly where the classic page puts
   them, which is the standing rule. The footer, the Feedback button, the "Do Not Sell" toast and
   the cookie banner are handled by the shared `.rf-theme.rf-no-tabbar` rules above, which any
   page on that list gets. */
.rf-theme .lesson-page .player-celebrate {
    background: rgba(12, 8, 11, .72);
}

.rf-theme .lesson-page .player-celebrate-inner {
    background: var(--rf-panel-bg);
    border: 1px solid var(--rf-glass-border);
    color: var(--rf-text);
    box-shadow: var(--rf-glass-shadow);
}

/* "Routine counted! 4-day streak." Where classic pops it up, only repainted: with no floating
   bar there is nothing there to clear. */
.rf-theme .lesson-page .routine-toast {
    background: var(--rf-panel-bg);
    border: 1px solid var(--rf-glass-border);
    color: var(--rf-text);
}

/* ── The lesson's own text: a LIGHT READING CARD (Melodie, 2026-09-19) ───────────────────────
   `lesson.body_html` is content in the DATABASE, written by Allison in the inline editor below,
   for a white page. Audited read-only against staging on 2026-09-19: 481 lessons carry text, 440
   of them with no colour of their own; NO lesson uses a highlight colour; of the 41 that name an
   ink, 34 use the app's own pink. Eight are a dark colour that disappears on this base, and the
   colours carry meaning (different members' quotes in different colours). No stylesheet can tell
   those apart, and the set changes whenever Allison writes a lesson, so the text keeps a light
   surface of its own and her colours go on reading exactly as she chose them. Rejected: lighting
   the whole page and lifting a failing ink at render time (it works, but it alters her markup on
   the way out and changes the colour she picked), and editing the eight lessons by hand (it
   leaves the next one to be written broken).

   The card is LIGHTER than the lightest point of the classic page's own background (its body
   gradient's lightest stop, with the faint grid overlay on top of it), so no DARK ink, in any
   lesson, present or future, reads worse here than it does today. (An ink lighter than about
   #F8F8FA would read worse on a lighter card; nothing in the catalogue is, and an editor whose
   default is near-black is unlikely to produce one, but the claim is not unconditional and a
   reviewer was right to say so.) A test does that arithmetic from the
   real stylesheets rather than from a list of colours that would rot. It cannot make an ink read
   BETTER than today either: the two lessons whose green and whose blue already fail on the white
   page still fail here, unchanged, and that is what carrying the page forward means. */
.rf-theme .lesson-page .player-body {
    --rf-reading-card: #FEFCF8;
    background: var(--rf-reading-card);
    color: var(--text-dark);
    border-radius: 20px;
    padding: 1.6rem 1.35rem;
    box-shadow: var(--rf-glass-shadow);
    /* The surface is light, so anything the BROWSER draws inside it (a scrollbar, a form control
       in a future lesson, the text selection) must be drawn light too. The page as a whole says
       `color-scheme: dark`, and that is inherited. */
    color-scheme: light;
}

/* HANDED BACK, not repainted. `.rf-theme p { color: inherit }` at the top of this file is there
   so a paragraph takes the colour of whatever it sits in; inside the card that makes Allison's
   paragraphs the near-black `--text-dark` instead of the `--text-medium` grey the CLASSIC page
   gives them (`style.css` colours every bare <p>). It is the same class of silent repaint the
   routine player's instruction line fell to, and the measured test is what found it here.
   Handed back its exact classic value: the card must reproduce today's page, not improve it. */
.rf-theme .lesson-page .player-body p {
    color: var(--text-medium);
}

/* A lesson whose saved HTML draws NOTHING gets no card. `:empty` was the first answer and it was
   wrong: it does not match an element that contains markup, and 67 of the 481 lessons with a
   body_html hold an empty wrapper the course import left behind, with no words and no picture.
   Every member on those would see a blank cream box. No selector can ask whether an element's
   descendants contain text, so the answer is computed where the page is rendered
   (`refaced.body_is_blank`) and the template puts this class on. 26 more lessons have no words
   but DO have a picture: those keep the card, which is what a picture needs. */
.rf-theme .lesson-page .player-body--blank {
    background: none;
    box-shadow: none;
    padding: 0;
}

/* ...until she starts writing one. Edit mode puts a paragraph into an empty body, and the class
   is decided when the page is RENDERED, so without this she would be typing the classic grey ink
   straight onto the plum page: measured at 2.22:1 by an adversarial reviewer, 2026-09-19. The
   card comes back the moment the editor is open, which is also what she is about to see members
   get. Admins only; a member never has `.editing-active` on anything. */
.rf-theme .lesson-page .player-body--blank.editing-active {
    background: var(--rf-reading-card);
    box-shadow: var(--rf-glass-shadow);
    padding: 1.6rem 1.35rem;
}

/* Classic pinks the links in a lesson and underlines them. Kept exactly, because the card is the
   classic surface: on it the link measures what it measures today. (It is 3.1:1 there, under the
   4.5:1 bar, on this page and on the classic one alike. Pre-existing, listed for Melodie, NOT
   changed here: it is the same wording and the same colour members read today.) */
.rf-theme .lesson-page .player-body a {
    color: #fc3cad;
}

/* Classic's own `--primary-strong`, named literally: the theme repoints that token to #B5126C,
   so `var(--primary-strong)` here would be a different pink from the one the classic card shows.
   Found by an adversarial reviewer, 2026-09-19. */
.rf-theme .lesson-page .player-body a:hover {
    color: #d81f8f;
}

/* Edit mode outlines whatever is editable, and those two areas are the ones she actually types
   in. Classic's `rgba(252,60,173,.55)` measures 2.03:1 on white and the first version of this
   rule measured 1.99:1 on the dark page: neither is a ring a keyboard user can see, and a
   reviewer pointed out that "every control in the editor draws the same ring" had quietly left
   out the two regions that matter most.

   Both take the gold, including the one around the light card. The card's outline is drawn at
   `outline-offset: 6px`, so it is OUTSIDE the card, on the dark page: a first attempt gave it
   the classic pink "because it sits on the card", and the measurement said 2.95:1, which is
   where that reasoning was wrong. */
.rf-theme .lesson-page .editing-active {
    outline-color: var(--rf-glow);
}

/* ── "Your answers" (two lessons carry one today, 19 text boxes and one multiple choice) ──── */
.rf-theme .lesson-page .assessment {
    background: var(--rf-glass-bg);
    border: 1px solid var(--rf-glass-border);
    box-shadow: var(--rf-glass-shadow);
    -webkit-backdrop-filter: blur(20px) saturate(150%);
    backdrop-filter: blur(20px) saturate(150%);
}

.rf-theme .lesson-page .assessment-title {
    color: var(--rf-text);
}

.rf-theme .lesson-page .assessment-sub {
    color: var(--rf-text-muted);
}

.rf-theme .lesson-page .assessment-prompt,
.rf-theme .lesson-page .assessment-choice {
    color: var(--rf-text);
}

.rf-theme .lesson-page .assessment textarea {
    background: rgba(255, 255, 255, .08);
    border: 1px solid rgba(244, 236, 241, .55);
    color: var(--rf-text);
}

.rf-theme .lesson-page .assessment textarea::placeholder {
    color: var(--rf-text-muted);
}

.rf-theme .lesson-page .assessment textarea:focus,
.rf-theme .lesson-page .assessment textarea:focus-visible {
    outline: 2px solid var(--rf-glow);
    outline-offset: 2px;
    border-color: transparent;
}

.rf-theme .lesson-page .assessment-choice input {
    accent-color: var(--rf-action);
}

.rf-theme .lesson-page .assessment .btn-primary {
    background: var(--rf-action);
    color: #fff;
    border: 0;
    box-shadow: none;
}

.rf-theme .lesson-page .assessment .btn-primary:focus-visible {
    outline: 2px solid var(--rf-glow);
    outline-offset: 3px;
}

/* Classic's success green (#3d9b4f) is 2.8:1 on this base. */
.rf-theme .lesson-page .assessment-status {
    color: #7ED9A0;
}

/* ── The inline editor. Admins only; it never renders for a member. ───────────────────────── */
.rf-theme .lesson-page .edit-toggle-label {
    color: var(--rf-text-muted);
}

.rf-theme .lesson-page .edit-toggle-track {
    background: rgba(255, 255, 255, .22);
}

.rf-theme .lesson-page .edit-toggle input:checked + .edit-toggle-track {
    background: var(--rf-action);
}

.rf-theme .lesson-page .edit-toolbar {
    background: var(--rf-panel-bg);
    border-color: var(--rf-glass-border);
    box-shadow: var(--rf-glass-shadow);
}

.rf-theme .lesson-page .edit-toolbar button,
.rf-theme .lesson-page .edit-toolbar select {
    background: rgba(255, 255, 255, .08);
    border-color: rgba(244, 236, 241, .4);
    color: var(--rf-text);
}

.rf-theme .lesson-page .edit-toolbar button:hover {
    border-color: var(--rf-action);
    background: rgba(222, 27, 135, .18);
}

/* The highlight swatch shows the colour it applies, so it keeps its pale yellow and needs dark
   ink on it; the classic rule sets that background with !important and nothing else. `button` is
   in the selector on purpose: without it the rule above (`.edit-toolbar button`) out-ranks this
   one and the letter goes light-on-yellow. Caught by looking at the page, not by a test. */
.rf-theme .lesson-page .edit-toolbar button.edit-color--hl {
    color: #101018;
}

.rf-theme .lesson-page .edit-pop {
    background: var(--rf-panel-bg);
    border-color: var(--rf-glass-border);
}

.rf-theme .lesson-page .edit-hex {
    background: rgba(255, 255, 255, .08);
    border-color: rgba(244, 236, 241, .4);
    color: var(--rf-text);
}

/* The two hex boxes say "#hex" until she types. Nothing in either stylesheet colours that, so
   it was the browser's own default, which `color-scheme: dark` repaints: named here instead of
   left to it. Found by measuring; a selector-reading test could not have seen it. */
.rf-theme .lesson-page .edit-hex::placeholder {
    color: var(--rf-text-muted);
}

.rf-theme .lesson-page .edit-toolbar-sep {
    background: rgba(255, 255, 255, .22);
}

.rf-theme .lesson-page .edit-routine-flag {
    color: var(--rf-text);
}

.rf-theme .lesson-page .edit-btn-save {
    background: var(--rf-action) !important;
    border-color: var(--rf-action) !important;
}

.rf-theme .lesson-page .edit-btn-cancel {
    color: var(--rf-text-muted) !important;
}

.rf-theme .lesson-page .edit-status {
    color: #7ED9A0;
}

.rf-theme .lesson-page .edit-status.err {
    color: #FFA8A1;
}

/* Every control in the editor draws the same ring as the rest of the page. `style.css` sets
   `outline: none` on input, textarea and select when focused and puts a faint pink glow there
   instead, which was written for a white page; the toolbar's buttons had only the browser's own
   1px default. The checkbox inside the Edit-mode switch is visually hidden, so its ring goes on
   the track it controls. Found by an adversarial reviewer measuring them, 2026-09-19. */
.rf-theme .lesson-page .edit-toolbar button:focus-visible,
.rf-theme .lesson-page .edit-toolbar select:focus-visible,
.rf-theme .lesson-page .edit-toolbar input:focus-visible,
.rf-theme .lesson-page .edit-routine-flag input:focus-visible,
.rf-theme .lesson-page .assessment-choice input:focus-visible {
    outline: 2px solid var(--rf-glow);
    outline-offset: 2px;
}

.rf-theme .lesson-page .edit-toggle input:focus-visible + .edit-toggle-track {
    outline: 2px solid var(--rf-glow);
    outline-offset: 2px;
}
