/* ============================================================================
 * portal.css — shared portal look & feel
 *
 * Load AFTER assets/css/theme.min.css so we can override Around-theme defaults
 * where they hurt readability in light mode. Every portal page links this.
 *
 * Scope: visual polish + contrast fixes ONLY. Page-specific styles still live
 * inline on their page.
 * ========================================================================= */


/* ------- Cross-page loading overlay -------
 * The portal is a multi-page static site (each HTML file is its own page
 * load), so every navigation has a brief window where the browser has
 * painted raw HTML but Vue hasn't compiled the template yet. Without
 * intervention the user sees:
 *   - `{{ variables }}` mustache syntax flash before Vue mounts
 *   - Blank / half-rendered tiles while initial API calls return
 *
 * Strategy: scope the scrim to JUST the content column (.col-lg-9 inside
 * .page-wrapper), so the header and sidebar stay visible while the body
 * loads. Makes navigation feel anchored instead of whole-page flashing.
 * portal-common.js adds `.portal-ready` to <body> when ready, and the
 * scrim fades out. min-height on the column during the loading state
 * stops the scrim collapsing to 0 before Vue renders any content.
 *
 * Fallback: pages without the standard .page-wrapper layout (first-time
 * setup, signin, marketing templates) don't get a scrim here. They're
 * light enough that the portal-common.js auto-hide is enough on its own.
 *
 * [v-cloak] covers the mustache flash specifically — any Vue element
 * with v-cloak will be invisible until Vue strips the attribute at mount.
 * ---------------------------------------------------------------------- */
.page-wrapper .col-lg-9 {
    position: relative;
}

/* Reserve vertical space while loading so the scrim has something to
 * cover — otherwise an empty column before Vue compile shows 0-height
 * pseudo-elements. Removed once .portal-ready lands, so the column
 * snaps back to its natural height. */
body:not(.portal-ready) .page-wrapper .col-lg-9 {
    min-height: 70vh;
}

/* White scrim + spinner, anchored inside the content column */
.page-wrapper .col-lg-9::before {
    content: '';
    position: absolute;
    inset: 0;
    background: var(--ar-body-bg, #ffffff);
    z-index: 10;
    opacity: 1;
    transition: opacity .25s ease-out, visibility .25s ease-out;
    visibility: visible;
    pointer-events: none;
}

.page-wrapper .col-lg-9::after {
    content: '';
    position: absolute;
    top: 40%;
    left: 50%;
    width: 42px;
    height: 42px;
    margin: -21px 0 0 -21px;
    border: 3px solid rgba(0, 0, 0, .08);
    border-top-color: var(--ar-primary, #4c82f7);
    border-radius: 50%;
    animation: portal-spin .8s linear infinite;
    z-index: 11;
    opacity: 1;
    transition: opacity .25s ease-out, visibility .25s ease-out;
    visibility: visible;
    pointer-events: none;
}

body.portal-ready .page-wrapper .col-lg-9::before,
body.portal-ready .page-wrapper .col-lg-9::after {
    opacity: 0;
    visibility: hidden;
}

@keyframes portal-spin {
    to { transform: rotate(360deg); }
}

/* Hide Vue elements until compiled — prevents {{ var }} flash. Pages
 * can opt in by adding `v-cloak` to any Vue-bound element. No-op on
 * pages that don't add the attribute. */
[v-cloak] {
    visibility: hidden;
}

/* Auto-hide cursor after inactivity. Class is toggled on <html> by
 * portal-common.js after CURSOR_HIDE_AFTER_MS of no mouse / keyboard
 * activity, removed instantly on the next input event.
 *
 * Why descendant selector + !important: Bootstrap and browser defaults
 * set explicit `cursor: pointer` on <a>, <button>, etc. A bare
 * `html.cursor-hidden { cursor: none }` would lose to those rules
 * because cursor is inherited but explicitly-set descendants win. The
 * `*` descendant selector + !important forces every element to none.
 *
 * Effect is invisible on a desktop (mouse moves keep resetting the
 * timer) but cleans up the unmoving cursor on a kiosk where the user
 * walked away or input is touch-only. */
html.cursor-hidden,
html.cursor-hidden * {
    cursor: none !important;
}


/* ------- Consistent bordered cards across the whole portal -------
 * The Dashboard was built with .tile-card / .info-card (both bordered,
 * rounded, subtle). Other portal pages use `card border-0` from Around's
 * template — deliberately flat against the page background. That looked
 * fine in isolation but inconsistent with the Dashboard once both shipped.
 * This override re-enables a soft 1px border + rounded corners on every
 * content card inside .page-wrapper so the visual language is unified.
 *
 * The !important is required to beat Bootstrap's .border-0 utility,
 * which also uses !important. Scoped to .page-wrapper so marketing
 * templates under /components and the theme's landing pages aren't
 * affected.
 * ------------------------------------------------------------------ */
.page-wrapper .card.border-0 {
    border: 1px solid var(--ar-border-color) !important;
    border-radius: .75rem !important;
}


/* ------- Soft badges (bg-opacity-10) -------
 * Around/Bootstrap give us e.g. `badge bg-secondary bg-opacity-10 text-secondary`
 * which in light mode is pale-grey text on a pale-grey pill — invisible.
 * Coloured variants each use a readable foreground + a subtle border so they
 * pop off cards without screaming.
 * -------------------------------------------- */
.badge.bg-opacity-10 {
    font-weight: 600;
    padding: .35em .65em;
    border: 1px solid transparent;
    line-height: 1.35;
}

.badge.bg-secondary.bg-opacity-10 {
    color: var(--ar-body-color) !important;
    background-color: rgba(0, 0, 0, .07) !important;
    border-color: rgba(0, 0, 0, .08);
}

.badge.bg-primary.bg-opacity-10 {
    color: var(--ar-primary) !important;
    border-color: rgba(var(--ar-primary-rgb), .2);
}

.badge.bg-success.bg-opacity-10 {
    color: var(--ar-success) !important;
    border-color: rgba(var(--ar-success-rgb), .2);
}

.badge.bg-info.bg-opacity-10 {
    color: var(--ar-info) !important;
    border-color: rgba(var(--ar-info-rgb), .2);
}

.badge.bg-warning.bg-opacity-10 {
    color: var(--ar-warning) !important;
    border-color: rgba(var(--ar-warning-rgb), .25);
}

.badge.bg-danger.bg-opacity-10 {
    color: var(--ar-danger) !important;
    border-color: rgba(var(--ar-danger-rgb), .2);
}


/* ------- Sidebar role badge -------
 * Populated by portal-common.js. The template uses `bg-secondary ...` which
 * is fixed above, but give the role badge specifically a more presentable
 * treatment — it's the ONE thing that tells you which tenant scope you have.
 * ----------------------------------- */
[data-portal-role] {
    display: inline-block;
    padding: .18rem .6rem;
    margin-top: .35rem;
    background: rgba(var(--ar-primary-rgb), .1);
    color: var(--ar-primary) !important;
    border: 1px solid rgba(var(--ar-primary-rgb), .22);
    border-radius: 10rem;
    font-size: .7rem;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    /* Undo badge-ish classes coming in from the template (bg-secondary/opacity etc). */
    background-color: rgba(var(--ar-primary-rgb), .1) !important;
}


/* ------- Heartbeat-style chips -------
 * When heartbeat is null/never, the code returns a bg-secondary chip. Make the
 * "never" state legible (inherits from the .bg-secondary.bg-opacity-10 fix),
 * but also lift the font-weight a touch so it reads as a real status.
 * -------------------------------------- */


/* ------- Empty-state rows -------
 * Lists/grids use `text-body-secondary` with wide vertical padding. Tune the
 * colour so it's clearly muted but not vanishing.
 * -------------------------------- */
.text-body-secondary {
    color: var(--ar-secondary-color, #6c757d) !important;
}


/* ------- Tables -------
 * Around's table-hover + .align-middle is OK; nudge header casing so list
 * pages read like a dashboard rather than a wall of bold text.
 * -------------- */
.table > thead > tr > th {
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--ar-secondary-color, #6c757d);
    border-bottom-width: 1px;
}


/* ------- Modals -------
 * The default modal header is flat. Add a subtle rule so the title doesn't
 * merge visually with the form below.
 * -------------- */
.modal-content .modal-header {
    border-bottom: 1px solid var(--ar-border-color);
}


/* ------- Tile cards (folders, screens, anything at-a-glance) -------
 * A reusable "tile" — the whole card is the click target, with an icon badge
 * on the left, primary + secondary text, a meta pill, and hover-revealed
 * action buttons top-right. Cards feel like a unit instead of a stack of rows.
 * -------------------------------------------------------- */
.tile-card {
    position: relative;
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: 1rem;
    min-height: 96px;
    padding: 1rem 1.15rem;
    background: var(--ar-card-bg, #fff);
    border: 1px solid var(--ar-border-color);
    border-radius: .75rem;
    color: inherit;
    text-decoration: none !important;
    transition: border-color .15s, box-shadow .2s, transform .15s;
}
.tile-card:hover {
    border-color: var(--ar-primary);
    box-shadow: 0 6px 18px -6px rgba(var(--ar-primary-rgb), .25);
    transform: translateY(-1px);
    color: inherit;
}

.tile-icon {
    width: 48px; height: 48px;
    display: inline-flex; align-items: center; justify-content: center;
    background: rgba(var(--ar-primary-rgb), .1);
    color: var(--ar-primary);
    border-radius: .55rem;
    font-size: 1.5rem;
    flex-shrink: 0;
}

.tile-body { min-width: 0; }    /* required so text-truncate works inside a grid cell */
.tile-title {
    font-size: 1rem; font-weight: 600;
    line-height: 1.25; margin: 0;
    color: var(--ar-heading-color, var(--ar-body-color));
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tile-subtitle {
    margin-top: .15rem;
    font-size: .8rem;
    color: var(--ar-secondary-color, #6c757d);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.tile-meta {
    flex-shrink: 0;
    display: inline-flex; align-items: center; gap: .35rem;
    padding: .3rem .7rem;
    background: rgba(0, 0, 0, .06);
    color: var(--ar-body-color);
    border-radius: 10rem;
    font-size: .75rem; font-weight: 600;
}

/* Action buttons top-right — always visible but ghosty, scale up on hover
 * for desktop and stay legible on touch. Positioned absolute so they don't
 * bump the grid layout. */
.tile-actions {
    position: absolute; top: .55rem; right: .55rem;
    display: flex; gap: .15rem;
    opacity: .5;
    transition: opacity .15s;
}
.tile-card:hover .tile-actions { opacity: 1; }
.tile-actions .btn {
    --ar-btn-padding-x: .45rem;
    --ar-btn-padding-y: .2rem;
    --ar-btn-font-size: .8rem;
    background: transparent;
    border-color: transparent;
    color: var(--ar-secondary-color, #6c757d);
}
.tile-actions .btn:hover {
    background: rgba(0, 0, 0, .08);
    border-color: transparent;
}
.tile-actions .btn.text-danger:hover {
    background: rgba(var(--ar-danger-rgb), .1);
    color: var(--ar-danger) !important;
}


/* ------- Info cards (screens, folders, anything "at-a-glance" with
 * title + subtitle + meta row). Vertical variant of .tile-card, styled
 * to match the Dashboard's stat-tile: left accent stripe, icon badge,
 * bold title, hover lift. Whole card is a click target.
 *
 * Colour the accent stripe + icon bg per-card by adding one of the
 * .accent-* modifier classes alongside .info-card.
 * ----------------------------------------------------- */
.info-card {
    position: relative;
    display: flex; flex-direction: column;
    padding: 1.1rem 1.2rem;
    min-height: 132px; height: 100%;
    background: var(--ar-card-bg, #fff);
    border: 1px solid var(--ar-border-color);
    border-radius: .8rem;
    color: inherit; text-decoration: none !important;
    overflow: hidden;
    transition: border-color .15s, box-shadow .2s, transform .15s;
    --card-accent: var(--ar-primary);
    --card-accent-rgb: var(--ar-primary-rgb);
}
.info-card::before {
    content: "";
    position: absolute; left: 0; top: 0; bottom: 0;
    width: 4px;
    background: var(--card-accent);
}
.info-card:hover {
    border-color: var(--card-accent);
    box-shadow: 0 8px 22px -8px rgba(var(--card-accent-rgb), .28);
    transform: translateY(-2px);
    color: inherit;
}

.info-card-head {
    display: flex; align-items: flex-start; gap: .75rem;
    margin-bottom: .7rem;
    /* No padding-right needed: action buttons used to live in the top-
       right corner and fight with long titles, but they now sit in the
       meta row at the bottom of the card, so the title can use the
       full card width. */
}
.info-card-icon {
    width: 40px; height: 40px; flex-shrink: 0;
    display: inline-flex; align-items: center; justify-content: center;
    background: rgba(var(--card-accent-rgb), .12);
    color: var(--card-accent);
    border-radius: .55rem;
    font-size: 1.2rem;
}
.info-card-title-wrap { flex: 1; min-width: 0; }
.info-card-title {
    font-size: 1.02rem; font-weight: 700;
    color: var(--ar-heading-color, var(--ar-body-color));
    line-height: 1.2; margin: 0;
    /* Wrap to up to 2 lines with ellipsis on overflow. Single-line
       ellipsis was too aggressive — folder names like "Chelmsford
       Front Counter — Spring Promo" would hard-truncate mid-phrase
       with no way to see the rest on a touch device. Two lines
       covers almost every real-world folder name; the 3+ line edge
       case still falls back to ellipsis + the Bootstrap tooltip
       (see refreshTooltips in MediaOverview.html). */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    overflow-wrap: anywhere;
}
.info-card-subtitle {
    margin-top: .2rem;
    font-size: .78rem;
    color: var(--ar-secondary-color, #6c757d);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* Status chip inside the card body — dot + label, colour via modifier class. */
.info-card-status {
    display: inline-flex; align-items: center; gap: .4rem;
    font-size: .78rem; font-weight: 500;
    margin-bottom: .6rem;
    color: var(--ar-body-color);
}
.info-card-status .status-dot {
    width: .55rem; height: .55rem; border-radius: 50%;
    background: var(--ar-success);
}
.info-card-status.is-warn .status-dot { background: var(--ar-danger); animation: pulse 1.6s ease-in-out infinite; }
.info-card-status.is-idle .status-dot { background: var(--ar-secondary-color, #6c757d); }

/* Meta row at bottom of the card (model, res, MAC, counts, etc.)
 * Soft dashed divider so it reads as secondary. */
.info-card-meta {
    margin-top: auto;
    padding-top: .55rem;
    border-top: 1px dashed var(--ar-border-color);
    display: flex; flex-wrap: wrap; gap: .5rem .9rem;
    font-size: .74rem;
    color: var(--ar-secondary-color, #6c757d);
}
.info-card-meta > span { display: inline-flex; align-items: center; gap: .3rem; }
.info-card-meta .mono { font-family: "SFMono-Regular", Menlo, Consolas, monospace; font-size: .7rem; }

/* Action buttons — live inline inside the card's meta row, right-aligned.
 *
 * Earlier incarnation used absolute positioning in the card's top-right
 * corner with hover-to-reveal. That broke on touch (no hover to trigger
 * the reveal) and ate title space on every card whether actions existed
 * or not. Sitting in the meta row below the dashed divider means:
 *   - always visible on every device
 *   - title gets the full card width
 *   - no pointer-capability media queries needed
 */
.info-card-actions {
    display: flex; gap: .15rem;
    /* ms-auto in markup handles right alignment in the meta row. */
}
.info-card-actions .btn {
    --ar-btn-padding-x: .4rem;
    --ar-btn-padding-y: .15rem;
    --ar-btn-font-size: .82rem;
    background: transparent;
    border-color: transparent;
    color: var(--ar-secondary-color, #6c757d);
    /* The meta row sits below a dashed divider — keep the buttons
       tight vertically so they don't push the card taller. */
    line-height: 1;
}
.info-card-actions .btn:hover { background: rgba(0, 0, 0, .08); border-color: transparent; color: var(--ar-body-color); }
.info-card-actions .btn.text-danger:hover { background: rgba(var(--ar-danger-rgb), .1); color: var(--ar-danger) !important; }

/* Accent palette — mirrors the dashboard t-* classes but uses --card-accent
 * so everything inside info-card (stripe, icon bg, hover shadow) picks up
 * from one place. */
.info-card.accent-primary { --card-accent: var(--ar-primary); --card-accent-rgb: var(--ar-primary-rgb); }
.info-card.accent-info    { --card-accent: var(--ar-info);    --card-accent-rgb: var(--ar-info-rgb); }
.info-card.accent-success { --card-accent: var(--ar-success); --card-accent-rgb: var(--ar-success-rgb); }
.info-card.accent-warning { --card-accent: var(--ar-warning); --card-accent-rgb: var(--ar-warning-rgb); }
.info-card.accent-danger  { --card-accent: var(--ar-danger);  --card-accent-rgb: var(--ar-danger-rgb); }
.info-card.accent-idle    { --card-accent: #9ca3af;           --card-accent-rgb: 156, 163, 175; }
.info-card.accent-violet  { --card-accent: #8b5cf6;           --card-accent-rgb: 139, 92, 246; }

@keyframes pulse {
    0%, 100% { opacity: 1;  transform: scale(1); }
    50%      { opacity: .4; transform: scale(1.2); }
}


/* ------- Asset cards (media library grid) -------
 * Thumbnail on top, compact body with name + meta, action row with ghost
 * buttons. Same visual language as .tile-card but portrait (vertical)
 * instead of horizontal.
 * ---------------------------------------------------- */
.asset-card {
    display: flex; flex-direction: column;
    height: 100%;
    background: var(--ar-card-bg, #fff);
    border: 1px solid var(--ar-border-color);
    border-radius: .6rem;
    overflow: hidden;
    transition: border-color .15s, box-shadow .2s, transform .15s;
}
.asset-card:hover {
    border-color: var(--ar-primary);
    box-shadow: 0 6px 16px -6px rgba(var(--ar-primary-rgb), .22);
    transform: translateY(-1px);
}

.asset-thumb {
    position: relative;
    aspect-ratio: 1;
    background: var(--ar-body-bg);
    overflow: hidden;
}
.asset-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.asset-thumb .asset-placeholder {
    position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    font-size: 2.5rem; color: var(--ar-body-color); opacity: .35;
}
.asset-type {
    position: absolute; top: .5rem; left: .5rem; z-index: 2;
    display: inline-flex; align-items: center;
    padding: .22rem .55rem;
    background: rgba(0, 0, 0, .6);
    color: #fff;
    border-radius: .35rem;
    font-size: .65rem; font-weight: 600;
    letter-spacing: .05em;
    text-transform: uppercase;
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}
.asset-type.asset-type--image { background: rgba(var(--ar-primary-rgb), .88); }
.asset-type.asset-type--video { background: rgba(var(--ar-info-rgb), .88); }

.asset-body {
    padding: .7rem .8rem .35rem;
    flex-grow: 1;               /* push actions to bottom even when name is short */
}
.asset-name {
    font-size: .875rem; font-weight: 600;
    color: var(--ar-heading-color, var(--ar-body-color));
    line-height: 1.3;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.asset-meta {
    margin-top: .15rem;
    font-size: .72rem;
    color: var(--ar-secondary-color, #6c757d);
}

.asset-actions {
    display: flex; justify-content: flex-end; gap: .1rem;
    padding: .2rem .5rem .55rem;
}
.asset-actions .btn {
    --ar-btn-padding-x: .5rem;
    --ar-btn-padding-y: .3rem;
    --ar-btn-font-size: .85rem;
    background: transparent;
    border-color: transparent;
    color: var(--ar-secondary-color, #6c757d);
    box-shadow: none;
}
.asset-actions .btn:hover,
.asset-actions .btn:focus-visible {
    background: rgba(0, 0, 0, .08);
    border-color: transparent;
    color: var(--ar-body-color);
}
.asset-actions .btn.text-danger:hover,
.asset-actions .btn.text-danger:focus-visible {
    background: rgba(var(--ar-danger-rgb), .1);
    color: var(--ar-danger) !important;
}


/* ------- Form input groups -------
 * Intentionally minimal. Earlier versions restyled the input-group-text here
 * and broke Around's careful radius/join rules, producing a visible "hairline"
 * between the addon and the input. Let the theme own the borders; we only
 * tint the addon's icon colour.
 * --------------------- */
.input-group-text > i {
    color: var(--ar-secondary-color, #6c757d);
}

/* =======================================================================
 * Shared portal footer
 *
 * Markup lives in /footer.html, auto-injected into #footerWrapper on every
 * app page by portal-common.js. Styles here are the bare minimum — the
 * structure relies on Bootstrap utility classes so dark mode just works.
 *
 * Intentionally no sticky-bottom behaviour. Pinning the footer to the
 * viewport bottom on short pages (half-filled Dashboard, empty list
 * states) looks worse than letting it sit right after the content.
 * ===================================================================== */
.portal-footer {
    /* Lift off the content so the border-top reads as a divider rather
       than an afterthought jammed under the last card. */
    margin-top: 3rem;
    background-color: transparent;
    color: var(--ar-secondary-color, #6c757d);
}
/* Brand wordmark inside the copyright string — small uppercase tracking
   in the primary colour gives the line a touch of identity without the
   cost of loading the full logo image here. */
.portal-footer-brand {
    color: var(--ar-primary);
    font-weight: 600;
    letter-spacing: .01em;
}
.portal-footer-link {
    color: var(--ar-secondary-color, #6c757d);
    text-decoration: none;
    transition: color .15s ease;
}
.portal-footer-link:hover,
.portal-footer-link:focus {
    color: var(--ar-primary);
    text-decoration: none;
}
