/* Tutorial helper — the floating "one step at a time" panel.
   Markup:    app/templates/partials/step-panel.html (mounted site-wide from the header)
   Behaviour: app/static/js/step-panel.js

   Extracted out of the partial on purpose. The panel is mounted on every page
   that shows the header, so inline it re-downloaded ~50KB on every navigation
   for an always-on iPad on care-home wifi. As a static file it is cached once.

   CSS variables only — zero literal colours. Every facility can rebrand
   --orange / --card-bg, and a hex that reads well on Oskoole orange in light
   mode is exactly the one that disappears on a facility branded navy at 2am. */

/* z-index 19400: above the tutorial progress banner (19000) and above the
   full-screen modal backdrops (10000) — the whole point of the panel is to
   follow the step WHILE the page is being used. Below the video help player
   (19500), the lightbox (20000) and the toasts (20001). */
.osp {
    position: fixed;
    z-index: 19400;
    font-family: inherit;
}
.osp.hidden { display: none !important; }
.osp * { box-sizing: border-box; }

/* -- geometry per state ---------------------------------------------- */
.osp.is-collapsed { left: auto; right: 1.5rem; width: 420px; max-width: calc(100vw - 3rem); }
.osp.is-expanded  { left: 1.5rem; right: 1.5rem; width: auto; max-width: 560px; margin-inline: auto; }

/* -- docking: two positions only, no dragging and no resizing --------- */
.osp.osp-bottom { bottom: max(1.5rem, env(safe-area-inset-bottom)); top: auto; }
/* 4.5rem clears the 60px sticky .navbar with a deliberate gap: the top dock
   stays a detached floating card (all four corners rounded, at every width)
   rather than pretending to be a navbar dropdown. Unlike the bottom dock there
   is no internal top safe-area inset anywhere in this file, i.e. no flush-top
   design was ever written. */
.osp.osp-top    { top: max(4.5rem, env(safe-area-inset-top)); bottom: auto; }

.osp .osp-bar, .osp .osp-sheet { display: none; }
.osp.is-collapsed .osp-bar { display: flex; }
.osp.is-expanded .osp-sheet { display: flex; }

/* The house floating-card style. */
.osp-bar, .osp-sheet {
    background: var(--card-bg);
    border: 1px solid var(--card-border);
    border-radius: 14px;
    box-shadow: var(--card-shadow-hover);
    overflow: hidden;
}
/* --card-border is nearly invisible in dark mode under a floating card. */
[data-theme="dark"] .osp { border-color: var(--border-color); }
[data-theme="dark"] .osp-bar,
[data-theme="dark"] .osp-sheet { border-color: var(--border-color); }

.osp-sr-only {
    position: absolute; width: 1px; height: 1px;
    padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* -- COLLAPSED state --------------------------------------------------- */
.osp-bar {
    align-items: center;
    gap: .6rem;
    height: 72px;
    padding: 0 .4rem 0 .75rem;
    cursor: pointer;
}
.osp-chip {
    flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    width: 44px; height: 44px; border-radius: 50%;
    background: var(--orange); color: var(--brand-on-primary);
    font-size: 1.15rem; font-weight: 700; line-height: 1;
}
.osp-bar-text { flex: 1 1 auto; min-width: 0; }
.osp-bar-progress {
    display: block;
    font-size: 1.05rem; font-weight: 700; color: var(--text-primary);
    line-height: 1.2;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.osp-bar-title {
    display: block;
    font-size: .95rem; color: var(--text-secondary);
    line-height: 1.25; margin-top: .1rem;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.osp-show {
    flex-shrink: 0;
    min-height: 44px; padding: 0 1rem;
    background: var(--orange); color: var(--brand-on-primary);
    border: none; border-radius: 10px;
    font-family: inherit; font-size: .95rem; font-weight: 700;
    cursor: pointer;
}
.osp-show:hover { background: var(--orange-hover); color: var(--brand-on-primary); }

/* -- EXPANDED state ---------------------------------------------------- */
.osp-sheet {
    flex-direction: column;
    max-height: min(72vh, 620px);
    /* dvh keeps the foot of the panel clear of the iPadOS toolbar; declared
       afterwards as a progressive enhancement (Safari < 15.4 drops it). */
    max-height: min(72dvh, 620px);
}
.osp-head {
    flex: 0 0 auto;
    display: flex; align-items: center; gap: .35rem;
    height: 56px; padding: 0 .35rem 0 .9rem;
    border-bottom: 1px solid var(--card-border);
}
.osp-prompt {
    flex: 1 1 auto; min-width: 0;
    font-size: .95rem; font-weight: 700; color: var(--text-primary);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.osp-icon {
    flex-shrink: 0;
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px; padding: 0;
    background: none; border: none; border-radius: 10px;
    color: var(--text-secondary); cursor: pointer;
    font-family: inherit; font-size: 1.15rem; line-height: 1;
}
.osp-icon:hover { background: var(--orange-light); color: var(--orange); }
.osp-icon svg {
    width: 20px; height: 20px; fill: none; stroke: currentColor;
    stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round;
}

/* A full-width, clearly labelled row (never an icon-only button, unlike the
   Move/Minimize/Close trio above it) — this is a rare "leave the mini view"
   action, not a frequent one, so it earns a visible label rather than an
   icon this audience would have to learn. Opens the tutorial's own full page
   and closes the mini panel; see step-panel.js's `dismiss: false` note on
   why closing here must NOT suppress the panel auto-opening again later. */
.osp-open-tutorial {
    flex: 0 0 auto;
    display: flex; align-items: center; justify-content: center; gap: .5rem;
    width: 100%; min-height: 48px; padding: .55rem 1rem;
    background: var(--input-bg); color: var(--text-primary);
    border: none; border-bottom: 1px solid var(--card-border);
    font-family: inherit; font-size: .92rem; font-weight: 600;
    cursor: pointer;
}
.osp-open-tutorial:hover { background: var(--orange-light); color: var(--orange); }
.osp-open-tutorial svg {
    width: 18px; height: 18px; fill: none; stroke: currentColor;
    stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; flex-shrink: 0;
}

/* Only this region scrolls: the head and the foot never leave the screen. */
.osp-body {
    flex: 1 1 auto; min-height: 0;
    overflow-y: auto; overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
    padding: .9rem 1rem 1rem;
}
.osp-heading {
    font-size: 1.35rem; font-weight: 700; color: var(--text-primary);
    line-height: 1.25;
}
.osp-heading:focus { outline: none; }
.osp-step-title {
    font-size: 1.1rem; font-weight: 600; color: var(--text-secondary);
    line-height: 1.3; margin-top: .15rem;
    overflow-wrap: anywhere;
}
.osp-progress {
    height: 6px; margin: .75rem 0;
    background: var(--progress-bg); border-radius: 999px; overflow: hidden;
}
.osp-progress-fill {
    height: 100%; width: 0;
    background: var(--orange); border-radius: 999px;
    transition: width .25s ease;
}
.osp-narration {
    font-size: 1.05rem; line-height: 1.6; color: var(--text-primary);
    overflow-wrap: anywhere;
}
/* The screenshot frame becomes role="button" only when a lightbox exists on
   the page, so it is styled as a plain block and never as a control. */
.osp-shot {
    display: block; width: 100%;
    margin-top: .85rem; padding: 0;
    background: none; border: none;
}
.osp-shot img {
    display: block; width: 100%; max-height: 30vh;
    object-fit: contain; border-radius: 10px;
}
.osp-shot.is-tappable { cursor: zoom-in; }
.osp-shot.is-tappable img { cursor: zoom-in; }

.osp-audio {
    display: flex; align-items: center; gap: .6rem;
    margin-top: .9rem; padding: .5rem .6rem;
    background: var(--audio-bg); border-radius: 12px;
}
.osp-play {
    flex-shrink: 0;
    display: inline-flex; align-items: center; justify-content: center;
    width: 56px; height: 56px; padding: 0;
    background: var(--orange); color: var(--brand-on-primary);
    border: none; border-radius: 50%; cursor: pointer;
}
.osp-play:hover { background: var(--orange-hover); color: var(--brand-on-primary); }
.osp-play svg { width: 22px; height: 22px; fill: currentColor; stroke: none; }
.osp-audio-mid { flex: 1 1 auto; min-width: 0; }
.osp-audio-time {
    display: block; font-size: .85rem; color: var(--text-secondary);
    line-height: 1.2; margin-bottom: .3rem;
}
/* No scrubber on purpose: an informational bar, not an interactive one. */
.osp-audio-bar {
    display: block;
    height: 5px; background: var(--progress-bg);
    border-radius: 999px; overflow: hidden;
}
.osp-audio-fill { display: block; height: 100%; width: 0; background: var(--orange); border-radius: 999px; }

.osp-linkbtn {
    display: flex; align-items: center; justify-content: center;
    width: 100%; min-height: 48px; margin-top: .9rem; padding: .5rem 1rem;
    background: transparent; color: var(--text-primary);
    border: 1px solid var(--input-border); border-radius: 12px;
    font-family: inherit; font-size: 1rem; font-weight: 600;
    text-align: center; text-decoration: none; cursor: pointer;
}
.osp-linkbtn:hover { border-color: var(--orange); color: var(--orange); }

.osp-nav {
    flex: 0 0 auto;
    display: flex; gap: .5rem;
    padding: .55rem .6rem max(.55rem, env(safe-area-inset-bottom));
    border-top: 1px solid var(--card-border);
    background: var(--card-bg);
}
.osp-nav-btn {
    flex: 1 1 50%; min-width: 0; min-height: 56px;
    background: var(--orange); color: var(--brand-on-primary);
    border: none; border-radius: 12px;
    font-family: inherit; font-size: 1.05rem; font-weight: 700;
    cursor: pointer;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.osp-nav-btn:hover { background: var(--orange-hover); color: var(--brand-on-primary); }
/* Unavailable Back is [aria-disabled], never [disabled]: the HTML attribute
   removes the button from the accessibility tree, so a VoiceOver user would
   never learn Back exists, and it cannot take focus — which also puts the
   ArrowLeft/ArrowRight shortcuts out of reach. So it stays a real, focusable
   button and only LOOKS unavailable. Secondary ink on the card background
   rather than muted-on-muted: this audience has to be able to read it. */
.osp-nav-btn[aria-disabled="true"] {
    background: transparent;
    color: var(--text-secondary);
    border: 1px solid var(--input-border);
    cursor: default;
}
.osp-nav-btn[aria-disabled="true"]:hover {
    background: transparent;
    color: var(--text-secondary);
}

.osp-msg { text-align: center; padding: .5rem 0 0; }
.osp-msg p { font-size: 1.05rem; line-height: 1.5; color: var(--text-primary); }

.osp .hidden { display: none !important; }

/* -- entrance and the "you are back on this tab" ring ------------------ */
.osp.osp-anim { animation: ospSlideIn .28s cubic-bezier(.22,1,.36,1) both; }
.osp.osp-return .osp-bar,
.osp.osp-return .osp-sheet { animation: ospRing 2.5s ease-out 1 both; }
.osp.osp-return .osp-chip { transform: scale(1.08); transition: transform .3s ease; }

@keyframes ospSlideIn { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: translateY(0); } }
@keyframes ospRing {
    0%   { box-shadow: var(--card-shadow-hover), 0 0 0 3px var(--orange); }
    85%  { box-shadow: var(--card-shadow-hover), 0 0 0 3px var(--orange); }
    100% { box-shadow: var(--card-shadow-hover); }
}

.osp :focus-visible { outline: 2px solid var(--orange); outline-offset: 2px; }

/* -- responsive: usable at 320px wide and 560px tall -------------------- */
/* At phone width the bottom dock is a real bottom sheet: full-bleed on the
   left, the right AND the bottom edge. Square lower corners floating 24px
   clear of the edge is not a coherent shape, `left: 0; right: 0` at this width
   IS the iOS bottom-sheet idiom residents already recognise, and going flush
   buys back 24px of vertical room on a 560px-tall screen.

   partials/head.html deliberately does NOT set viewport-fit=cover, so every
   env(safe-area-inset-*) below currently resolves to 0. Each one therefore
   carries a literal minimum, so the internal padding still keeps content off
   the physical edge today and grows correctly if cover is ever turned on. */
@media (max-width: 520px) {
    .osp.is-collapsed, .osp.is-expanded {
        left: 0; right: 0; width: auto; max-width: none; margin-inline: 0;
    }
    .osp.osp-bottom { bottom: 0; }
    .osp.osp-bottom .osp-bar {
        height: calc(72px + env(safe-area-inset-bottom));
        padding-bottom: max(.5rem, env(safe-area-inset-bottom));
    }
    .osp.osp-bottom .osp-bar, .osp.osp-bottom .osp-sheet { border-radius: 14px 14px 0 0; }
    .osp-shot img { max-height: 26vh; }
    /* The progress banner lives bottom-left (19000). We own the nudge, exactly
       as video-help-player.html owns lifting the toasts, so the neighbour never
       has to know this feature exists. Measured from the screen edge now that
       the bar is flush against it. */
    body.osp-docked-bottom.osp-collapsed .tp-banner { bottom: calc(72px + env(safe-area-inset-bottom) + .5rem); }
}
/* Below ~400px the collapsed bar leaves the title about 68px, i.e. five
   characters and an ellipsis. The step number is the useful half; drop the
   title rather than ship a stub. */
@media (max-width: 400px) {
    .osp-bar-title { display: none; }
}
@media (max-height: 560px) {
    .osp-sheet {
        max-height: 88vh;
        /* Same two-step fallback as the base rule: Safari < 15.4 DROPS a
           declaration it cannot parse rather than degrading it, so without the
           vh line first the sheet would have no bound at all on an old iPad. */
        max-height: 88dvh;
    }
    .osp-shot img { max-height: 20vh; }
    .osp-narration { font-size: 1rem; }
}
@media (prefers-reduced-motion: reduce) {
    .osp.osp-anim { animation: none; }
    .osp.osp-return .osp-chip { transform: none; transition: none; }
    .osp-progress-fill { transition: none; }
    .osp.osp-return .osp-bar,
    .osp.osp-return .osp-sheet {
        animation: none;
        box-shadow: var(--card-shadow-hover), 0 0 0 3px var(--orange);
    }
}
