/* ═══════════════════════════════════════════════════════════════════════════
   card-phone.css · the device a project card runs an app inside
   ═══════════════════════════════════════════════════════════════════════════

   The portfolio grid runs live apps in its tiles rather than pictures of them.
   This is the frame those apps sit in, so that every phone card on /portfolio/
   is the same hardware and only the app inside differs.

   The reference is Kinetic's own phone (portfolio/design-systems/kinetic/,
   Layer 4 · Screens). That one is a real component of that design system and
   keeps its numbers in its own page CSS, where --sys-container-product means
   something. This file is how a prototype with no device frame of its own
   borrows it. Kinetic does not link this file: it carries the same two markers
   so scripts/verify-card-phone.mjs can hold every card to one geometry.

   ── the contract ──
   url      ?card=<screen id>&w=<stage px>&theme=<light|dark>
   head     <link rel="stylesheet" href="/portfolio/assets/css/card-phone.css">
            <script src="/portfolio/assets/js/card-phone.js"></script>
   markup   <div data-card-frame><div data-card-screen> the app </div></div>
            [data-card-hide] on anything that is workbench rather than app
   optional window.cardPhoneShow(name)  to activate the screen ?card= named
            window.cardPhoneTheme(mode)  when the app needs more than the
                                         data-bs-theme attribute to change ground
            --card-notch-clear           when the app's own content would
                                         otherwise run under the notch

   Paths are root-absolute because the pages that link this sit at several
   depths under /portfolio/; the site root is the document root in production
   and the server root locally, so one href works from all of them.

   ── why the device bleeds off the tile ──
   402x780 inside a 12px bezel is a 426x804 device. On a square tile laid out
   at w=652 with 14px above it, that puts the frame at about two thirds of the
   tile's width with its bottom 166px running off the edge. The overflow is the
   point: a device the tile cuts through reads as a photograph of one, where a
   device sitting whole inside the tile reads as a sticker of one. Anything
   pinned to the bottom of the screen goes off the edge with it. That is the
   trade the bleed asks for, and it is deliberate — do not shorten the screen
   to rescue a footer, because a screen short enough to keep one is a screen
   that no longer bleeds.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── the spec ──
   Six values. A card declares nothing else about the device. */
html[data-card-mode] {
    --card-pad: 14px;        /* stage px above the frame */
    --phone-bezel: 12px;     /* the frame's border, every side */
    --phone-surface: 402px;  /* the width the app lays itself out at */
    --phone-screen: 780px;   /* and its height: the app scrolls inside this */
    --card-ground: #f6f7f8;  /* the tile behind the device */
    --card-ink: #0e1119;     /* the frame's own dark */

    background: var(--card-ground);
}

/* Held back until the boot has picked the screen and the frame has taken over,
   so a card never shows the page's own chrome for a frame first. */
html[data-card-mode]:not([data-card-ready]) body {
    visibility: hidden !important;
}

/* The tile behind the device follows the page that holds the tile. Dark takes
   the raised surface rather than the page ground: on /portfolio/ the card is a
   raised thing measured against #111417, and a card painted at the page value
   reads as a hole cut in the page instead of a tile sitting on it. */
html[data-card-mode][data-card-theme="dark"] {
    --card-ground: #191b1d;
}

/* The crop belongs to <html>, which is the tile's own size and untransformed.
   Put it on the scaled body instead and the body is clipped against its own box
   and the clip is then scaled with it, so the device stops short of the edge. */
html[data-card-mode] {
    margin: 0 !important;
    padding: 0 !important;
    height: 100% !important;
    overflow: hidden !important;
}

/* The body is the stage: laid out at --card-w and scaled by the tile's width
   over that, so the scaled body spans the tile exactly and everything inside it
   is in stage coordinates. This is what lets the device keep a real 402px
   surface at any card size instead of reflowing into a narrow one. */
html[data-card-mode] body {
    margin: 0 !important;
    height: auto !important;
    overflow: visible !important;
    background: var(--card-ground) !important;
    width: var(--card-w) !important;
    transform: scale(var(--card-scale, 1));
    transform-origin: top left;
    display: flex !important;
    justify-content: center !important;
    align-items: flex-start !important;
    padding: var(--card-pad) 0 0 !important;
}

/* A card is a picture of the app, not of the workbench it is driven from. */
html[data-card-mode] [data-card-hide] {
    display: none !important;
}

/* ── the hardware ── */
html[data-card-mode] [data-card-frame] {
    flex: 0 0 auto !important;
    width: calc(var(--phone-surface) + var(--phone-bezel) * 2) !important;
    max-width: none !important;
    margin: 0 !important;
    padding: var(--phone-bezel) !important;
    border: 0 !important;
    border-radius: 50px !important;
    background: linear-gradient(150deg, #23283a, var(--card-ink)) !important;
    /* On the page the frame has a column beside it for separation. Alone on a
       tile of its own ground it has none, so the shadow carries the edge. */
    box-shadow: 0 26px 64px -20px rgba(14, 17, 25, .5),
                0 8px 22px -10px rgba(14, 17, 25, .28) !important;
    position: relative !important;
}

html[data-card-mode] [data-card-frame]::after {
    content: '';
    position: absolute;
    top: 21px;
    left: 50%;
    transform: translateX(-50%);
    width: 123px;
    height: 31px;
    border-radius: 50rem;
    background: var(--card-ink);
    z-index: 5;
}

/* ── the surface ──
   Laid out at its true size and cropped by the tile, never shrunk to fit it. */
html[data-card-mode] [data-card-screen] {
    width: var(--phone-surface) !important;
    height: var(--phone-screen) !important;
    max-width: none !important;
    min-height: 0 !important;
    margin: 0 !important;
    border: 0 !important;
    border-radius: 38px !important;
    box-shadow: none !important;
    padding-top: var(--card-notch-clear, 0) !important;
}

/* The scroll is real, its bar is not drawn: nothing scrolls a card. */
html[data-card-mode] [data-card-screen],
html[data-card-mode] [data-card-screen] * {
    scrollbar-width: none !important;
}

html[data-card-mode] [data-card-screen]::-webkit-scrollbar,
html[data-card-mode] [data-card-screen] *::-webkit-scrollbar {
    display: none !important;
}
