/* 12C3 photo pages. Palette and type scale are copied from the design pack,
   do not tweak them here or the page drifts away from the reference. */

:root {
    --bg: #F2F0EC;
    --card: #FFFFFF;
    --sheet: #F7F6F3;
    --ink: #12161A;
    --ink-soft: #4A4F54;
    --muted: #676C71;
    --line: rgba(18, 22, 26, .10);
    --line-soft: rgba(18, 22, 26, .07);
    --accent: #123A6B;
    --accent-dark: #0C2747;
    --like: #B23A2F;
    --dark: #0A0B0C;
    --serif: 'Noto Serif Display', 'Noto Serif Display Fallback A', 'Noto Serif Display Fallback B', Georgia, serif;
    --sans: 'Be Vietnam Pro', 'Be Vietnam Pro Fallback A', 'Be Vietnam Pro Fallback B', system-ui, -apple-system, sans-serif;
    --topbar: 58px;
}

* { box-sizing: border-box; }

/* Many blocks below set an explicit display, which overrides the HTML hidden attribute.
   Declare it once for the whole page instead of remembering it case by case. */
[hidden] { display: none !important; }

html, body { margin: 0; padding: 0; background: var(--bg); }

body {
    font-family: var(--sans);
    color: var(--ink);
    -webkit-font-smoothing: antialiased;
    /* No "pretty" wrapping here. It pulls words down to balance the last line, so a line can end
       well before the right edge while the next word would still have fitted. Plain wrapping fills
       every line to the edge, which is the house rule for this site. */
    text-wrap: wrap;
}

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-dark); }
button { font-family: inherit; }
img { display: block; }

/* Slim scrollbars are for a mouse only. Styling them on a touch screen turns the overlay bar
   into a real 6px column carved out of the visible width, so the page can be dragged sideways
   by those 6px and the whole layout wobbles left and right under the thumb. */
@media (hover: hover) and (pointer: fine) {
    ::-webkit-scrollbar { height: 6px; width: 6px; }
    ::-webkit-scrollbar-thumb { background: rgba(18, 22, 26, .18); border-radius: 3px; }
}

.no-bar { scrollbar-width: none; -ms-overflow-style: none; }
.no-bar::-webkit-scrollbar { display: none; }

@keyframes shimmer { 0% { opacity: .5 } 50% { opacity: .85 } 100% { opacity: .5 } }
@keyframes fade-up { from { opacity: 0; transform: translateY(8px) } to { opacity: 1; transform: none } }

/* --- Shared buttons --- */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    /* Browser buttons ship with a grey-white background. Without this reset, a button that
       only carries .btn ends up white text on white. */
    background: transparent;
    color: inherit;
    border: 1px solid transparent;
    border-radius: 999px;
    font-size: 13px;
    padding: 10px 18px;
    cursor: pointer;
    white-space: nowrap;
    transition: background .18s, border-color .18s, color .18s;
}

.btn-primary { background: var(--accent); color: #fff; font-weight: 500; }
.btn-primary:hover { background: var(--accent-dark); }
.btn-ghost { background: transparent; border-color: rgba(18, 22, 26, .18); color: var(--ink); }
.btn-ghost:hover { border-color: rgba(18, 22, 26, .34); }
.btn-sm { font-size: 12.5px; padding: 7px 13px; }

/* --- Top bar --- */

.topbar {
    position: sticky;
    top: 0;
    z-index: 40;
    background: rgba(242, 240, 236, .88);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    /* Most pages open with plain text on the same cream ground as the bar, so without this line
       the header melts into the page. Kept hairline thin and paler than the old one. */
    border-bottom: 1px solid rgba(18, 22, 26, .06);
}

.topbar-inner {
    position: relative;
    /* Three columns with the two outer ones equal: that is what puts the tab row on the exact
       middle of the bar no matter how long the name on the left runs. The zero minimum lets the
       name shorten instead of shoving the row off centre. */
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    align-items: center;
    gap: 14px;
    /* Same gutter as every content block below, otherwise the logo and the section titles are 24px apart. */
    padding: 10px clamp(16px, 4vw, 40px);
    min-height: var(--topbar);
    max-width: 1280px;
    margin: 0 auto;
}

/* The block gives way when the row runs short, otherwise the tabs are pushed off screen. */
.brand { display: flex; align-items: center; gap: 10px; flex: 1 1 0; min-width: 0; }
.brand img { width: 34px; height: 34px; border-radius: 50%; object-fit: cover; border: 1px solid rgba(18, 22, 26, .12); }
.brand-text { min-width: 0; }
/* Two stacked lines as in the design pack, not side by side. */
.brand-top { display: flex; align-items: center; gap: 8px; min-width: 0; }
.brand-name { font: 500 15px/1.15 var(--serif); color: var(--ink); white-space: nowrap; }
.brand-bar { width: 1px; height: 11px; background: rgba(18, 22, 26, .2); flex: 0 0 auto; }
/* The full school name shows at every width. It is the name of the school, so it is never
   trimmed with an ellipsis and never swapped for a shorter form: the room is taken from the
   account cluster on the right instead. */
.brand-school {
    /* Bold on purpose: the school name is the anchor of the header next to the badge. */
    font: 700 12.5px/1.15 var(--serif);
    color: var(--ink-soft);
    white-space: nowrap;
    min-width: 0;
}

/* The lower line is spaced out until it runs at least as wide as the line above it, so the two
   lines square off under the badge. The value is fixed because both lines are fixed text at a
   fixed size: it holds at every screen width. */
.brand-sub {
    display: block;
    margin-top: 3px;
    font-size: 10px;
    line-height: 1.15;
    color: var(--muted);
    text-transform: uppercase;
    white-space: nowrap;
    /* This line sets the width of the pair and the line above is squeezed to match it, in the
       script at the end of the header partial. Widened here because the design pack draws the
       school year spaced out; the plain spacing left it far shorter than the line above. */
    letter-spacing: .155em;
}

/* The 900 to 1179 band is the tightest one: the tab row shares the line, so the school name goes
   first even though the window is wider than a phone. */
/* Every width that still shows the tab row has to give the row its space. Below 720px the row is
   gone and the bottom bar takes over, so the school name comes back there. */
/* The design pack keeps this half of the line down to 900px, but its mock has no download button
   on the right. With that button in place the row still runs over at 924px, so these words go one
   step earlier. */
/* The name beside the round face is what eats the width the school name needs, and it goes as
   soon as the bar gets tight. The face alone still opens the same filter, and the name is one
   tap away in the account menu. */
@media (max-width: 899px) {
    .me-chip span { display: none; }
    .me-chip { width: 44px; padding: 0; justify-content: center; }
}

/* The narrowest phones still sold. Everything on the right gives up a few pixels so the school
   name keeps its place. */
@media (max-width: 360px) {
    .topbar-inner { padding: 8px 12px; gap: 6px; }
    /* A few pixels off the badge and the class name, so the school name stays whole here too. */
    .brand { gap: 8px; }
    .brand img { width: 30px; height: 30px; }
    .brand-name { font-size: 14px; }
    /* The photo pages carry one more button up here, so the school name gives up half a point
       of type rather than a single letter. */
    .brand-school { font-size: 12px; }
    .brand-sub { font-size: 9.5px; }
    .topbar-right { gap: 4px; }
    .topbar-right > .btn,
    .topbar-right > .account,
    .topbar-right > .me-chip,
    .me-chip,
    .btn-select { width: 40px; height: 40px; min-height: 40px; }
    .account-btn { width: 40px; height: 40px; }
}

/* The four tabs sit dead centre of the VIEWPORT. Letting them flex between the left and right
   clusters shifts their centre by the width of those clusters, which reads as crooked. */
.tabs {
    display: flex;
    justify-content: center;
    gap: 2px;
    /* Both side clusters grow from a zero basis, so whatever they hold the tab row still lands on
       the middle of the bar. Absolute centring did that too until the row grew wide enough to
       scroll, and then it read as crooked. */
    flex: 0 1 auto;
    min-width: 0;
    overflow-x: auto;
    overflow-y: hidden;
}

/* The right cluster mirrors the brand so the tab row sits dead centre; see .topbar-right below. */

/* Roomy padding only where the row has room for it. */
@media (min-width: 1180px) {
    .tabs button,
    .tabs a { padding: 8px 14px; }
}

/* Seven tabs no longer fit a narrow laptop at full size. Below that width the row leaves the
   centre of the bar and joins the flow instead: dead centre means it sits ON the buttons at the
   right, and the last tab ends up half under them. */
@media (max-width: 1179px) {
    .tabs button,
    .tabs a { font-size: 12px; padding: 8px 7px; }

    /* The logo already leads home, so that tab gives up its place to the seven others. */
    .tabs a[data-nav="home"] { display: none; }

    /* Same trick as on phones: the download button keeps its icon and drops the word. */
    .btn-select {
        width: 38px;
        padding: 0;
        border-radius: 50%;
        justify-content: center;
    }

    .btn-select .icon-only { display: block; }
    .btn-select .btn-select-label { display: none; }

}

.tabs button,
.tabs a {
    border: 0;
    background: transparent;
    color: #4A4F54;
    font-size: 13px;
    font-weight: 400;
    padding: 8px 10px;
    border-radius: 999px;
    cursor: pointer;
    white-space: nowrap;
    transition: background .18s, color .18s;
}

.tabs a { text-decoration: none; display: inline-flex; align-items: center; min-height: 36px; }
.tabs button:hover,
.tabs a:hover { color: var(--ink); }
/* Straight from the design palette: the open tab is dark blue text on a very light blue field,
   weight 600; the other tabs stay grey #4A4F54. */
.tabs button[aria-pressed="true"],
.tabs a[aria-current="page"] { background: rgba(18, 58, 107, .09); color: #123A6B; font-weight: 600; }

/* margin-left keeps pushing the cluster right even when the tab row is hidden on phones. */
.topbar-right { flex: 1 1 0; justify-content: flex-end; display: flex; align-items: center; gap: 8px; }

.me-chip {
    display: flex;
    align-items: center;
    gap: 8px;
    border: 1px solid var(--line);
    background: var(--card);
    border-radius: 999px;
    padding: 4px 14px 4px 4px;
    cursor: pointer;
    font-size: 12.5px;
    max-width: 190px;
}

.me-chip img { width: 28px; height: 28px; border-radius: 50%; object-fit: cover; flex: 0 0 auto; }

/* Google sign-in button and the account chip in the top bar. */
.btn-login { min-height: 44px; }

.account { position: relative; }

.account-btn {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    border: 1px solid var(--line);
    background: var(--accent);
    color: #fff;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}

.account-initial { font: 500 15px/1 var(--serif); text-transform: uppercase; }
.account-btn { overflow: hidden; padding: 0; }
.account-btn img { width: 100%; height: 100%; object-fit: cover; }

.account-menu {
    position: absolute;
    right: 0;
    top: 54px;
    width: 248px;
    background: #fff;
    border: 1px solid rgba(18, 22, 26, .1);
    border-radius: 16px;
    box-shadow: 0 18px 44px rgba(10, 20, 35, .16);
    overflow: hidden;
    z-index: 61;
    padding: 0;
}

/* The face gets a soft ring while its menu is open, so the open state reads at a glance. */
.account-btn[aria-expanded="true"] { box-shadow: 0 0 0 3px rgba(18, 58, 107, .28); }

.account-head {
    display: flex;
    align-items: center;
    gap: 11px;
    padding: 15px 16px 14px;
    border-bottom: 1px solid rgba(18, 22, 26, .08);
}

.account-head-face {
    width: 38px;
    height: 38px;
    flex: 0 0 38px;
    border-radius: 50%;
    object-fit: cover;
    background: var(--accent);
}

.account-head-initial {
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font: 500 16px/1 var(--serif);
    text-transform: uppercase;
}

/* min-width lets the two lines shrink; without it a long address pushes the box wider. */
.account-head-id { display: block; min-width: 0; }
.account-name { display: block; font: 500 15px/1.25 var(--serif); color: #12161A; }
.account-name, .account-mail { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.account-mail { display: block; font-size: 11.5px; color: #8A8F94; margin: 2px 0 0; }

.account-waiting {
    margin: 0;
    padding: 11px 16px 12px;
    border-bottom: 1px solid rgba(18, 22, 26, .08);
    font-size: 12px;
    line-height: 1.5;
    color: var(--muted);
}

.account-items { padding: 6px; }
.account-items form { margin: 0; }

.account-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    height: 44px;
    padding: 0 10px;
    border: 0;
    border-radius: 10px;
    background: none;
    font: 400 14px/1 var(--sans);
    color: #12161A;
    text-align: left;
    cursor: pointer;
}

.account-item svg { width: 17px; height: 17px; flex: 0 0 17px; stroke-width: 1.6; }
.account-item:hover { background: rgba(18, 58, 107, .07); }
.account-split { height: 1px; margin: 5px 10px; background: rgba(18, 22, 26, .08); }

/* Signing out sits lower in the visual order than the two links above it, on purpose. */
.account-quit { color: var(--muted); }
.account-quit:hover { background: rgba(18, 22, 26, .05); color: #12161A; }

/* Red dot mirrors the one on the phone tab bar: the class leader has something waiting. */
.account-dot {
    width: 8px;
    height: 8px;
    margin-left: auto;
    border-radius: 50%;
    background: #B3261E;
}

.me-chip span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* --- Hero --- */

/* The hero is a full-bleed dark blue block. Black text on the beige field looked flat and
   gave the page no focal point. */
/* Same frame as the home banner: capped width, side gutter, rounded corners. */
.hero-wrap { max-width: 1280px; margin: 0 auto; padding: clamp(12px, 1.6vw, 20px) clamp(16px, 4vw, 40px) 0; }

.hero {
    border-radius: 18px;
    background: #10305B;
    color: #fff;
    /* Equal padding top and bottom, adding up to the two old figures, so the text block sits on
       the middle of the picture. */
    padding: calc((clamp(30px, 5vw, 58px) + clamp(26px, 3.6vw, 44px)) / 2) clamp(16px, 4vw, 40px);
    text-align: center;
    position: relative;
    overflow: hidden;
}

/* A soft light streak so the blue block is not perfectly flat. */
.hero::before {
    content: '';
    position: absolute;
    left: 50%;
    top: -40%;
    width: min(150%, 1500px);
    aspect-ratio: 1;
    transform: translateX(-50%);
    border-radius: 50%;
    background: radial-gradient(circle, rgba(255, 255, 255, .10), rgba(255, 255, 255, 0) 62%);
    pointer-events: none;
}

/* The reunion hero wears the same photo backdrop as the home page; a flat blue field beside a
   photo-lined home page read as two different sites. */
.hero img.bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

/* The banner changes picture on its own. The second layer carries the picture coming in and
   fades over the one underneath, so the change reads as a soft cross fade, not a jump. */
.hero img.bg-next, .home-hero img.bg-next { opacity: 0; transition: opacity .9s ease-in-out; }
.hero img.bg-next.is-on, .home-hero img.bg-next.is-on { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
    .hero img.bg-next, .home-hero img.bg-next { transition: none; }
}
.hero-shade {
    position: absolute;
    inset: 0;
    /* A soft pool of shade behind the words, then a light wash over the whole picture: the faces
       underneath stay visible while the white type keeps something to sit on. */
    background:
        radial-gradient(ellipse 62% 58% at 50% 48%, rgba(6, 16, 32, .46), rgba(6, 16, 32, 0) 70%),
        linear-gradient(to bottom, rgba(8, 20, 40, .26), rgba(8, 20, 40, .56));
}

/* The wash over the picture is light enough now that the faces underneath read, so the words carry
   their own soft shadow instead of leaning on a dark sheet to stay legible. */
.hero-inner, .home-hero-inner { text-shadow: 0 1px 16px rgba(6, 16, 32, .72); }
.hero-inner .btn, .home-hero-inner .btn { text-shadow: none; }

.hero-inner { position: relative; max-width: 1280px; margin: 0 auto; }

/* The round badge used to sit at the top of this banner. It is gone now, but the banner keeps its
   height: the space it took, its own size plus the gap below it, moves into the padding. Split it
   evenly top and bottom, otherwise the whole text block sinks below the middle of the picture. */
.hero-inner {
    padding-top: calc((clamp(76px, 9vw, 104px) + 20px) / 2);
    padding-bottom: calc((clamp(76px, 9vw, 104px) + 20px) / 2);
}

.eyebrow { font-size: 11px; letter-spacing: .16em; text-transform: uppercase; color: var(--muted); margin-bottom: 14px; }
.hero .eyebrow { letter-spacing: .2em; color: rgba(255, 255, 255, .84); }

/* line-height 1.1, not 1: at 1 the diacritics and the tails of g and y clip into the next line. */
.hero h1 { font: 400 clamp(40px, 8.4vw, 84px)/1.1 var(--serif); margin: 0 0 14px; letter-spacing: -.02em; color: #fff; }

/* Line taken from the logo, thin rules on both sides, same language as the divider between teacher bands. */
.hero-tagline {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    margin-bottom: 14px;
}

.hero-tagline span { font: italic 400 15px/1.3 var(--serif); color: rgba(255, 255, 255, .94); }
.hero-tagline i { width: clamp(24px, 6vw, 64px); height: 1px; background: rgba(255, 255, 255, .45); }

.hero-meta { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px 22px; font-size: 14px; color: rgba(255, 255, 255, .9); margin-bottom: 26px; }
.hero-meta i { color: rgba(255, 255, 255, .3); font-style: normal; }

/* On a phone the three facts stay on one row: the type shrinks and the gaps close instead of
   dropping the roll call to a row of its own. Each fact never breaks in the middle. */
@media (max-width: 560px) {
    .hero-meta { gap: 4px 10px; font-size: 12.5px; }
    .hero-meta span { white-space: nowrap; }
}

@media (max-width: 379px) {
    .hero-meta { gap: 4px 8px; font-size: 11.5px; }
}

@media (max-width: 339px) {
    .hero-meta { gap: 4px 6px; font-size: 10.5px; }
}

/* The invitation line must never wrap to two rows on desktop, hence the 1180px box. */
/* Glass panel dropped: just one italic line and one button, centred. */
.hero-invite {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 18px;
    max-width: 1280px;
    margin: 0 auto;
    text-align: center;
}

.hero-invite p {
    font: italic 400 clamp(15px, 1.55vw, 21px)/1.5 var(--serif);
    color: rgba(255, 255, 255, .9);
    margin: 0;
}

@media (min-width: 560px) {
    .hero-invite p { white-space: nowrap; }
}

.hero-invite .btn { flex: 0 0 auto; }

/* White button with blue text so it stands out on the dark blue field. */
.hero-invite .btn-primary {
    background: #fff;
    color: #10305B;
    font: 600 14px/1 var(--sans);
    min-height: 48px;
    padding: 0 28px;
    border-radius: 999px;
    white-space: nowrap;
}

.hero-invite .btn-primary:hover { background: rgba(255, 255, 255, .88); }

/* display:flex overrides the hidden attribute, so it has to be spelled out. */
.hero-invite[hidden] { display: none; }

/* --- Horizontal rails (most liked, scenes and keepsakes) --- */

/* Breathing room above and below per the design pack: the old 6px glued the rail to the hero. */
/* The side gutter belongs to the block, not to the rail inside it: the scroller has to stop at
   the same edge as the banner, otherwise a picture slides out past the frame while it scrolls. */
.strip { padding: clamp(26px, 3.4vw, 38px) clamp(16px, 4vw, 40px) clamp(24px, 3.4vw, 36px); }

/* Centred rail heading with thin rules, same language as the line under the logo. */
.strip-head {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 0;
    margin-bottom: 16px;
    text-align: center;
}

.strip-head h2 { font: 400 22px/1 var(--serif); margin: 0; display: flex; align-items: center; gap: clamp(10px, 2vw, 18px); }
.strip-head h2::before,
.strip-head h2::after { content: ''; width: clamp(20px, 5vw, 54px); height: 1px; background: rgba(18, 22, 26, .16); }
.strip-head span { font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: #5F6469; margin-top: 9px; }
.strip-head p { font-size: 13.5px; color: #5F6469; margin: 9px auto 0; }

/* The scroller is centred with width:max-content plus auto margins. justify-content:center
   clips the first cell once the row overflows. scroll-snap is off too: with snap the browser
   jumps to the first cell edge and eats the left gutter. */
.strip-scroll { overflow-x: auto; }

.strip-rail {
    display: flex;
    gap: 8px;
    width: max-content;
    max-width: 100%;
    /* Left aligned, not centred. Auto margins on a row wider than its scroller resolve to a
       negative left margin, so the rail started 46px outside the frame and the first picture
       came in already cut. */
    margin: 0;
    padding: 0 0 6px;
}

/* A most-liked cell larger than a grid cell dominates the page, smaller than that disappears.
   200px sits just under the 237px of a six-column grid cell. */
/* Most shots from that day are landscape, a square cell crops the people at both edges. */
.strip-tile { flex: 0 0 auto; cursor: pointer; }
.strip-tile .frame { position: relative; width: 260px; height: 195px; border-radius: 4px; overflow: hidden; background: rgba(18, 22, 26, .07); }
.strip-tile img { width: 100%; height: 100%; object-fit: cover; }

/* Other frames of the same burst on the single photo page: two per row on a phone, four on a
   wide screen. A rail hid the last frame past the edge of the screen. */
.burst-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
    max-width: 1280px;
    margin: 0 auto 10px;
}

@media (min-width: 720px) { .burst-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px; } }

.burst-cell {
    position: relative;
    display: block;
    aspect-ratio: 4 / 3;
    border-radius: 6px;
    overflow: hidden;
    background: rgba(18, 22, 26, .07);
}

.burst-cell img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

/* Meet-up albums only: the class manager can drop a photo straight from the page. */
.tile-wrap { position: relative; }

.tile-drop {
    position: absolute;
    top: 8px;
    right: 8px;
    border: 0;
    border-radius: 999px;
    padding: 5px 11px;
    background: rgba(10, 12, 14, .62);
    color: #fff;
    font: 500 11.5px/1 var(--sans);
    cursor: pointer;
}

.tile-drop:hover { background: #B3261E; }
.tile-drop[disabled] { opacity: .5; cursor: default; }
.burst-cell:hover img { opacity: .92; }

/* Rank 01, 02, 03 in the top left corner, so it reads as a leaderboard. */
.strip-rank {
    position: absolute;
    left: 0;
    top: 0;
    font: 400 13px/1 var(--serif);
    color: #fff;
    padding: 7px 11px;
    background: linear-gradient(135deg, rgba(10, 12, 14, .55), rgba(10, 12, 14, 0));
}

@media (max-width: 560px) {
    .strip-tile .frame { width: 208px; height: 156px; }
}

.strip-like {
    position: absolute;
    right: 8px;
    bottom: 8px;
    display: flex;
    align-items: center;
    gap: 5px;
    background: rgba(10, 12, 14, .68);
    color: #fff;
    padding: 4px 9px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 500;
}

/* Scene shots are mostly landscape, so the cell is wider than a most-liked cell but the same height. */
.scene-tile { flex: 0 0 auto; width: 280px; height: 200px; border-radius: 10px; overflow: hidden; cursor: pointer; background: rgba(18, 22, 26, .07); }
.scene-tile img { width: 100%; height: 100%; object-fit: cover; }

.section-split { margin-top: 34px; padding: clamp(28px, 4vw, 44px) 0 60px; }
.section-intro { padding: 0 clamp(16px, 4vw, 40px); margin-bottom: 16px; }
.section-intro h2 { font: 400 24px/1.15 var(--serif); margin: 0 0 8px; }
.section-intro p { margin: 0; font-size: 13.5px; line-height: 1.6; color: var(--muted); }

/* --- Sort bar --- */

/* The sort bar is NOT sticky. The page allows exactly two sticky bars, the top bar and the
   sub-tab bar; a third one stacks three rows on top of each other. */
.sortbar {
    padding: 22px clamp(16px, 4vw, 40px) 18px;
    max-width: 1280px;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
}

/* Four chips read as sorting on their own, no lead-in label needed. */
.sortbar-chips { display: flex; gap: 6px; overflow-x: auto; max-width: 100%; }

.sortbar button {
    flex: 0 0 auto;
    border: 1px solid rgba(18, 22, 26, .16);
    background: transparent;
    color: #4A4F54;
    font-size: 13px;
    min-height: 36px;
    padding: 0 15px;
    border-radius: 999px;
    cursor: pointer;
    white-space: nowrap;
}

.sortbar button[aria-pressed="true"] { background: #12161A; border-color: #12161A; color: #fff; font-weight: 500; }

/* --- Full-bleed photo grid --- */

/* The photo grid spans the whole viewport instead of the 1280px band: all four reunion sub-tabs
   are photos. syncGridCols() in photos.js derives the column count from the real width so a cell
   stays near 224px; auto-fill would blow up to six huge cells on a 27" screen. 6 is the fallback. */
/* The column count also lives in CSS, not only in syncGridCols(): the grid holds server drawn
   placeholder cells before any script runs, and with the JavaScript value missing they would lay
   out five across on a phone and the wall would jump the moment the script corrected it. Keep the
   two breakpoints in step with syncGridCols(). */
:root { --photo-cols: 5; }
@media (max-width: 899px) { :root { --photo-cols: 4; } }
@media (max-width: 559px) { :root { --photo-cols: 2; } }

.photo-grid {
    /* Columns instead of a square grid: a portrait shot stays portrait and a landscape one stays
       wide, the way the photographer framed it. A square crop cut heads off often enough that the
       whole wall read as careless. */
    column-count: var(--photo-cols, 5);
    /* Same breathing room as the Drive wall: pictures pressed together read as one grey slab,
       and the class kept asking why this page looked heavier than that one. */
    column-gap: 10px;
    padding: 10px;
}

@media (max-width: 560px) {
    .strip-tile, .strip-tile .frame { width: 168px; }
    .strip-tile .frame { height: 168px; }
    .scene-tile { width: 230px; height: 168px; }
}

.tile {
    position: relative;
    background: rgba(18, 22, 26, .07);
    cursor: pointer;
    overflow: hidden;
    /* On the profile page a tile is a link, and a link flows inline like text: the bottom margin
       is dropped, so the pictures clump together, and the rounded corner cannot clip the image
       inside. Declaring it a block fixes both. */
    display: block;
    /* A cell must not be split across two columns, and it needs its own bottom gap because the
       column layout only spaces columns, not the cells inside one. */
    break-inside: avoid;
    margin-bottom: 10px;
    border-radius: 4px;
}

/* Height comes from the real ratio, set inline while the cell is built. Cells with no ratio yet
   fall back to a wide frame so the wall does not jump once the picture lands. */
.tile:not([style*="aspect-ratio"]) { aspect-ratio: 3 / 2; }

.tile img { display: block; width: 100%; height: 100%; object-fit: cover; }
.tile.skeleton { animation: shimmer 1.4s ease-in-out infinite; }

/* White text over a photo: a flat scrim is not enough, a bright outdoor shot swallows the text.
   A gradient darkening towards the bottom plus a text shadow stays readable on light and dark shots. */
.tile-meta {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 14px 9px 7px;
    display: flex;
    align-items: center;
    gap: 12px;
    background: linear-gradient(to top, rgba(10, 12, 14, .72), rgba(10, 12, 14, 0));
    color: #fff;
    text-shadow: 0 1px 3px rgba(10, 12, 14, .8);
    font-size: 11px;
    letter-spacing: .01em;
    transition: background .18s;
}

.tile:hover .tile-meta { background: linear-gradient(to top, rgba(10, 12, 14, .88), rgba(10, 12, 14, .12)); }
.tile-meta span { display: flex; align-items: center; gap: 4px; }
body.no-meta .tile-meta { display: none; }

.tile-burst {
    position: absolute;
    top: 8px;
    right: 8px;
    background: rgba(10, 12, 14, .72);
    color: #fff;
    font-size: 10.5px;
    font-weight: 500;
    padding: 3px 8px;
    border-radius: 6px;
    letter-spacing: .02em;
}

.tile-check {
    position: absolute;
    top: 8px;
    left: 8px;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    border: 1.5px solid #fff;
    background: rgba(10, 12, 14, .5);
    box-shadow: 0 1px 4px rgba(10, 12, 14, .5);
    display: none;
    align-items: center;
    justify-content: center;
    color: #fff;
}

body.selecting .tile-check { display: flex; }


/* The tick sits on top of the photo button, so taps have to pass straight through it. Without
   this, pressing the circle itself does nothing while pressing beside it works. */
.drive-cell .tile-check { pointer-events: none; }
.tile[data-selected="true"] .tile-check,
.drive-cell[data-selected="true"] .tile-check { background: var(--accent); border-color: var(--accent); }
.tile[data-selected="true"]::after,
.drive-cell[data-selected="true"]::after {
    content: '';
    position: absolute;
    inset: 0;
    border: 3px solid var(--accent);
    pointer-events: none;
}

.grid-more { display: flex; justify-content: center; padding: 34px 16px 10px; }

/* The load more button is now only a marker telling us the reader reached the end of the wall:
   photos arrive on scroll, as on the shared moments tab, so the button needs no face. It still
   has to sit in the flow rather than display none, or its position cannot be measured. */
#loadMore.is-auto {
    opacity: 0;
    pointer-events: none;
    height: 1px;
    padding: 0;
    border: 0;
}

/* --- Empty states --- */

.empty-state { max-width: 520px; margin: 0 auto; padding: clamp(60px, 14vw, 120px) 24px; text-align: center; }
.empty-state .ring {
    width: 60px;
    height: 60px;
    border-radius: 50%;
    border: 1px solid rgba(18, 22, 26, .16);
    margin: 0 auto 22px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--accent);
}
.empty-state h2 { font: 400 27px/1.2 var(--serif); margin: 0 0 12px; }
.empty-state p { margin: 0 0 24px; font-size: 14px; line-height: 1.65; color: var(--muted); }

.page-head { padding: clamp(26px, 5vw, 48px) clamp(16px, 4vw, 40px) 20px; }

/* The teachers intro is centred to match the three teacher bands below it.
   A person album and the profile page stay left aligned because of the button row on the right. */
[data-panel="teachers"] .page-head { margin: 0 auto; text-align: center; }

[data-panel="teachers"] .page-head h1 {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: clamp(10px, 2vw, 18px);
    margin: 0;
}

[data-panel="teachers"] .page-head h1::before,
[data-panel="teachers"] .page-head h1::after {
    content: '';
    width: clamp(20px, 5vw, 54px);
    height: 1px;
    background: rgba(18, 22, 26, .16);
}

[data-panel="teachers"] .page-head p { margin: 12px auto 34px; color: #5F6469; }
.page-head h1 { font: 400 clamp(28px, 5vw, 44px)/1.1 var(--serif); margin: 0 0 10px; }
.page-head p { margin: 0 0 4px; font-size: 14px; color: var(--muted); line-height: 1.6; }
.page-head-row { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 18px; }
.page-head-actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* --- Teachers tab --- */

/* One band per teacher in its own frame: a five photo mosaic on the left, the text on the right. */
.teacher-grid {
    display: flex;
    flex-direction: column;
    gap: 0;
    /* No foot padding here: the tab itself carries the bottom breathing room, and the subject
       teacher block below brings its own space above the rule. */
    padding: 0 clamp(16px, 4vw, 40px);
}

/* Between two teacher bands sits a beat carrying the words for the next school year, instead of a plain gap. */
.teacher-turn {
    display: flex;
    align-items: center;
    gap: clamp(14px, 2vw, 26px);
    padding: clamp(30px, 4.4vw, 58px) clamp(8px, 4vw, 60px);
}

.teacher-turn .line { flex: 1; height: 1px; }
.teacher-turn .line.left { background: linear-gradient(to right, rgba(18, 58, 107, 0), rgba(18, 58, 107, .22)); }
.teacher-turn .line.right { background: linear-gradient(to left, rgba(18, 58, 107, 0), rgba(18, 58, 107, .22)); }

.teacher-turn .mark {
    display: flex;
    align-items: center;
    gap: 10px;
    color: #123A6B;
}

.teacher-turn .dot {
    width: 5px;
    height: 5px;
    background: rgba(18, 58, 107, .3);
    transform: rotate(45deg);
}

.teacher-turn .ring {
    width: 8px;
    height: 8px;
    border: 1px solid rgba(18, 58, 107, .5);
    transform: rotate(45deg);
}

.teacher-turn .label {
    font: italic 400 15px/1 var(--serif);
    letter-spacing: .04em;
    color: #4A6488;
    white-space: nowrap;
}

.teacher-block {
    display: flex;
    flex-wrap: wrap;
    background: var(--card);
    border: 1px solid rgba(18, 22, 26, .09);
    border-radius: 18px;
    overflow: hidden;
    /* min-height, not height: a flex item never shrinks below min-content, and a hard height
       with overflow:hidden would clip the photo count and both buttons underneath. */
    min-height: clamp(320px, 24vw, 430px);
}

.teacher-mosaic {
    position: relative;
    flex: 1 1 340px;
    align-self: stretch;
    min-height: 220px;
    display: grid;
    grid-template-columns: 1.7fr 1fr 1fr;
    grid-template-rows: 1fr 1fr;
    gap: 4px;
    background: rgba(18, 22, 26, .06);
}

/* The photo is positioned absolutely inside the cell: in normal flow a portrait shot would
   stretch the whole row, making one teacher band twice as tall as the other two. */
.mosaic-cell { position: relative; border: 0; padding: 0; background: rgba(18, 22, 26, .07); cursor: pointer; overflow: hidden; }
.mosaic-cell img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.mosaic-cell.m1 { grid-column: 1; grid-row: 1 / 3; }
.mosaic-cell.m2 { grid-column: 2; grid-row: 1; }
.mosaic-cell.m3 { grid-column: 3; grid-row: 1; }
.mosaic-cell.m4 { grid-column: 2; grid-row: 2; }
.mosaic-cell.m5 { grid-column: 3; grid-row: 2; }

.teacher-grade {
    position: absolute;
    top: 14px;
    left: 14px;
    background: rgba(255, 255, 255, .94);
    color: var(--accent);
    font-size: 11.5px;
    font-weight: 500;
    letter-spacing: .08em;
    text-transform: uppercase;
    padding: 6px 12px;
    border-radius: 999px;
    box-shadow: 0 2px 10px rgba(10, 12, 14, .18);
    pointer-events: none;
}

.teacher-all {
    position: absolute;
    right: 12px;
    bottom: 12px;
    background: rgba(10, 12, 14, .6);
    color: #fff;
    font-size: 12px;
    padding: 8px 14px;
    border-radius: 999px;
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
}

.teacher-all:hover { background: rgba(10, 12, 14, .78); color: #fff; }

.teacher-text {
    flex: 0 0 clamp(300px, 34%, 520px);
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: clamp(24px, 2.6vw, 40px);
}

.teacher-head { display: flex; align-items: center; gap: 16px; margin-bottom: 18px; }
.teacher-number { font: 400 clamp(46px, 4.4vw, 64px)/.8 var(--serif); color: var(--accent); letter-spacing: -.04em; }
.teacher-meta { border-left: 1px solid rgba(18, 22, 26, .16); padding-left: 16px; }
.teacher-role { display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .18em; color: var(--accent); }
.teacher-years { display: block; font-size: 11px; letter-spacing: .14em; color: #5F6469; margin-top: 5px; }

.teacher-text h2 { margin: 0 0 14px; }
.teacher-salute { display: block; font: 500 11px/1 var(--sans); text-transform: uppercase; letter-spacing: .2em; color: #5F6469; margin-bottom: 8px; }
.teacher-text h2 em { font: italic 400 clamp(22px, 2.05vw, 30px)/1.2 var(--serif); font-style: italic; }

.teacher-story {
    font-size: 15px;
    line-height: 1.7;
    color: var(--ink-soft);
    margin: 0 0 16px;
}
.teacher-count { font-size: 13px; color: var(--muted); margin-bottom: 20px; }
.teacher-actions { display: flex; flex-wrap: wrap; gap: 10px; }
.teacher-actions .btn { min-height: 46px; }

/* Once the band wraps, the text sits underneath and must take the full width. Keeping the fixed
   column width here would leave a white gap on the right for the whole story. */
@media (max-width: 700px) {
    .teacher-text { flex: 1 1 100%; padding: 22px 18px 24px; }
    /* Left aligned on purpose. Justifying stretched the gaps between words so wide that the text
       looked combed apart on a phone screen. */
    .teacher-story { text-align: left; -webkit-hyphens: none; hyphens: none; }
}

/* Guest group on the roll call tab. */

.roll-guests { border-top: 1px solid var(--line); padding-top: 26px; padding-bottom: 60px; }
.roll-note { margin: 0 0 16px; font-size: 13.5px; color: var(--muted); line-height: 1.6; }

.people-grid, .guest-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 240px), 1fr));
    gap: 10px;
}

/* Teachers and guests share this card style, same size and aligned in the same columns. */
.people-card {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 11px 12px 11px 11px;
    background: var(--card);
    border: 1px solid rgba(18, 22, 26, .09);
    border-radius: 14px;
    cursor: pointer;
}

.people-card .face { width: 42px; height: 42px; border-radius: 50%; overflow: hidden; background: rgba(18, 22, 26, .07); flex: 0 0 auto; }
.people-card .face img { width: 100%; height: 100%; object-fit: cover; }
.people-card .body { min-width: 0; flex: 1 1 auto; }
.people-card .body b { display: block; font-size: 13.5px; font-weight: 500; line-height: 1.35; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.people-card .body span { display: block; font-size: 12px; color: var(--muted); margin-top: 2px; }

/* Copy album link button, so anyone can pass their own album on to someone else. */
.link-copy {
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--line);
    background: transparent;
    color: var(--muted);
    border-radius: 50%;
    cursor: pointer;
    transition: color .18s, border-color .18s, background .18s;
}

.link-copy:hover { color: var(--ink); border-color: rgba(18, 22, 26, .3); }
.link-copy.done { background: var(--accent); border-color: var(--accent); color: #fff; }
.link-copy.done svg { display: none; }
.link-copy.done::after { content: 'Đã chép'; font-size: 10.5px; font-weight: 500; }


/* Subject teachers, at the foot of the teachers tab: a quiet grid of small cards under a rule. */
.others-wrap {
    max-width: 1280px;
    margin: clamp(30px, 3.2vw, 44px) auto 0;
    padding: clamp(22px, 2.4vw, 30px) clamp(16px, 4vw, 40px) 0;
    border-top: 1px solid rgba(18, 22, 26, .14);
}

.others-head { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 18px; margin-bottom: clamp(22px, 2.6vw, 32px); }
.others-intro { flex: 1 1 420px; min-width: 0; }
.others-intro .eyebrow { font-size: 11px; letter-spacing: .2em; text-transform: uppercase; color: #5F6469; margin-bottom: 10px; }
.others-intro h2 { font: 400 clamp(24px, 2.8vw, 34px)/1.15 var(--serif); margin: 0 0 10px; }
.others-intro p { margin: 0; font-size: 14px; line-height: 1.65; color: #5F6469; }
.others-count { flex: 0 0 auto; display: flex; align-items: baseline; gap: 8px; }
.others-count b { font: 400 clamp(34px, 4vw, 52px)/1 var(--serif); color: var(--accent); font-weight: 400; }
.others-count span { font-size: 13.5px; color: #5F6469; }

.others-grid { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: clamp(10px, 1.2vw, 16px); }

@media (max-width: 1240px) { .others-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); } }
@media (max-width: 1000px) { .others-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 720px) { .others-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 480px) { .others-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.other-cell {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    background: var(--card);
    border: 1px solid rgba(18, 22, 26, .09);
    border-radius: 14px;
    padding: clamp(14px, 1.4vw, 18px) 10px;
}

.other-cell .face {
    width: clamp(56px, 5vw, 68px);
    height: clamp(56px, 5vw, 68px);
    border-radius: 50%;
    background: rgba(18, 22, 26, .07);
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    font: 400 21px/1 var(--serif);
    color: rgba(18, 22, 26, .42);
    margin-bottom: 11px;
}

.other-cell .face img { width: 100%; height: 100%; object-fit: cover; }
.other-cell .name { font-size: 13.5px; font-weight: 500; line-height: 1.35; }
.other-cell .subject { font-size: 12px; color: #5F6469; margin-top: 4px; }

/* --- Roll call --- */

.roll-head { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 24px; margin-bottom: 32px; }
.roll-score { display: flex; align-items: baseline; gap: 8px; margin-left: auto; }
.roll-score b { font: 400 clamp(44px, 9vw, 68px)/1 var(--serif); color: var(--accent); }
.roll-score span { font-size: 16px; color: var(--muted); }

.roll-bar { height: 4px; background: rgba(18, 22, 26, .09); border-radius: 2px; overflow: hidden; margin-bottom: 34px; }
.roll-bar i { display: block; height: 100%; background: var(--accent); }

.roll-label { font-size: 11px; letter-spacing: .16em; text-transform: uppercase; color: var(--muted); margin-bottom: 14px; }

/* Eleven per row: the 22 people present fill exactly two rows with no leftover cells. */
.roll-grid {
    display: grid;
    grid-template-columns: repeat(11, minmax(0, 1fr));
    gap: 12px;
    margin-bottom: 40px;
}

@media (max-width: 1400px) {
    .roll-grid { grid-template-columns: repeat(9, minmax(0, 1fr)); }
}

@media (max-width: 1080px) {
    .roll-grid { grid-template-columns: repeat(7, minmax(0, 1fr)); }
}

@media (max-width: 820px) {
    .roll-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}

@media (max-width: 560px) {
    .roll-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* Guests and teachers carry a much longer caption than the roll call ("Child of Tuyet Mai · 76
   photos"), so their cells get more room: fewer columns at every width, and a slightly wider gap.
   At eleven columns the caption broke into three cramped lines under every face. */
.roll-guests .roll-grid,
/* The band's own bottom padding already separates it from the next one, so the grid keeps no
   margin of its own: stacked, the two made a band of empty page under the last row. */
.roll-teachers .roll-grid { grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 18px; margin-bottom: 0; }

@media (max-width: 1400px) {
    .roll-guests .roll-grid,
    .roll-teachers .roll-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}

@media (max-width: 1080px) {
    .roll-guests .roll-grid,
    .roll-teachers .roll-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}

@media (max-width: 820px) {
    .roll-guests .roll-grid,
    .roll-teachers .roll-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

@media (max-width: 560px) {
    .roll-guests .roll-grid,
    .roll-teachers .roll-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.roll-cell { cursor: pointer; }
.roll-cell .face {
    position: relative;
    aspect-ratio: 1;
    border-radius: 12px;
    overflow: hidden;
    background: rgba(18, 22, 26, .07);
    margin-bottom: 9px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.roll-cell .face .initial { font: 400 clamp(28px, 3vw, 40px)/1 var(--serif); color: rgba(18, 22, 26, .22); }
.roll-cell .face img { width: 100%; height: 100%; object-fit: cover; }
.roll-cell .name { font-size: 13.5px; font-weight: 500; line-height: 1.3; }
.roll-cell .sub { font-size: 11.5px; color: #5F6469; margin-top: 2px; }

.roll-cell.absent { cursor: default; }
.roll-cell.absent .face { background: rgba(18, 22, 26, .045); border: 1px solid var(--line-soft); }
.roll-cell.absent .name { color: var(--muted); }
.roll-cell.absent .sub { color: rgba(18, 22, 26, .35); }

/* Space under the card, otherwise it touches the rule of the group below. */
.roll-teachers { border-top: 1px solid var(--line); padding-top: 26px; padding-bottom: 26px; }
/* --- Floating bar while selecting photos --- */

.select-bar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 45;
    padding: 12px clamp(12px, 3vw, 24px) calc(12px + env(safe-area-inset-bottom));
    background: rgba(18, 22, 26, .94);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    color: #fff;
}

.select-bar-inner { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; max-width: 1100px; margin: 0 auto; }
.select-bar b { font-size: 13.5px; font-weight: 500; flex: 0 0 auto; }
.select-bar .quick { display: flex; gap: 6px; flex: 1 1 auto; overflow-x: auto; }
.select-bar .end { display: flex; gap: 8px; flex: 0 0 auto; }

.btn-dark { border: 1px solid rgba(255, 255, 255, .28); background: transparent; color: #fff; }
.btn-dark:hover { border-color: rgba(255, 255, 255, .6); }
.btn-light { border: 0; background: #fff; color: var(--ink); font-weight: 600; }

.dl-row { max-width: 1100px; margin: 12px auto 0; padding-top: 12px; border-top: 1px solid rgba(255, 255, 255, .16); display: flex; align-items: center; gap: 12px; }
.dl-track { flex: 1; height: 3px; background: rgba(255, 255, 255, .2); border-radius: 2px; overflow: hidden; }
.dl-track i { display: block; height: 100%; background: #fff; transition: width .3s; }
.dl-row span { font-size: 12px; color: rgba(255, 255, 255, .8); white-space: nowrap; }

/* --- Photo viewer --- */

.viewer {
    position: fixed;
    inset: 0;
    z-index: 60;
    background: var(--dark);
    display: flex;
    flex-direction: column;
    animation: fade-up .18s ease-out;
}

.viewer[hidden] { display: none; }

.viewer-top { display: flex; align-items: center; gap: 14px; padding: 14px 16px; color: rgba(255, 255, 255, .72); font-size: 12.5px; flex: 0 0 auto; }
.viewer-top .sep { color: rgba(255, 255, 255, .3); }
.viewer-top .spacer { flex: 1; }

.icon-btn {
    border: 0;
    background: rgba(255, 255, 255, .10);
    color: #fff;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
}
.icon-btn:hover { background: rgba(255, 255, 255, .2); }
.icon-btn.big { width: 42px; height: 42px; }

.viewer-stage {
    flex: 1 1 auto;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 clamp(8px, 4vw, 60px);
    min-height: 0;
    overflow: hidden;
}

/* align-self: stretch makes the frame exactly as tall as the middle area. Without it the height
   follows the photo itself, and a portrait shot spills over the face row and the buttons below. */
.viewer-frame {
    position: relative;
    align-self: stretch;
    min-height: 0;
    max-width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
}

.viewer-frame img { height: 100%; width: auto; max-width: 100%; max-height: 100%; object-fit: contain; border-radius: 2px; }

/* The photo keeps its ratio, so its edges do not match the frame edges. JS sizes this layer to the
   real image area; only then do the face boxes land in the right place. */
.face-layer { position: absolute; pointer-events: none; }

.face-box {
    position: absolute;
    border: 2px solid #fff;
    border-radius: 3px;
    box-shadow: 0 0 0 9999px rgba(10, 11, 12, .42);
}

.face-box b {
    position: absolute;
    left: 0;
    top: 100%;
    margin-top: 6px;
    background: #fff;
    color: var(--ink);
    font-size: 11.5px;
    font-weight: 500;
    padding: 3px 9px;
    border-radius: 5px;
    white-space: nowrap;
}

.viewer-nav { position: absolute; top: 50%; transform: translateY(-50%); }
.viewer-nav.prev { left: 6px; }
.viewer-nav.next { right: 6px; }

.viewer-bottom { flex: 0 0 auto; padding: 12px clamp(12px, 4vw, 28px) calc(16px + env(safe-area-inset-bottom)); }

.face-rail { display: flex; gap: 10px; overflow-x: auto; padding: 4px 0 14px; }
.face-rail[hidden] { display: none; }
.face-chip { flex: 0 0 auto; width: 60px; cursor: pointer; text-align: center; background: none; border: 0; padding: 0; }
.face-chip .avatar {
    width: 52px;
    height: 52px;
    margin: 0 auto 5px;
    border-radius: 50%;
    background: rgba(255, 255, 255, .14);
    border: 2px solid transparent;
    overflow: hidden;
    opacity: .75;
    transition: opacity .18s, border-color .18s;
}
.face-chip .avatar img { width: 100%; height: 100%; object-fit: cover; }
.face-chip .name { font-size: 10.5px; color: #D8DDE3; line-height: 1.25; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.face-chip[aria-pressed="true"] .avatar { border-color: #fff; opacity: 1; }
.face-chip[aria-pressed="true"] .name { color: #fff; }

.viewer-ask {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    padding: 10px 14px;
    margin-bottom: 12px;
    background: rgba(255, 255, 255, .07);
    border-radius: 10px;
}
.viewer-ask[hidden] { display: none; }
.viewer-ask span { font-size: 13px; color: rgba(255, 255, 255, .9); flex: 1 1 auto; }

.viewer-actions { display: flex; align-items: center; gap: 8px; flex-wrap: nowrap; }
.viewer-actions .spacer { flex: 1; }
.viewer-actions .btn { background: rgba(255, 255, 255, .08); border-color: rgba(255, 255, 255, .24); color: #fff; }
.viewer-actions .btn:hover { border-color: rgba(255, 255, 255, .55); }
.viewer-actions .btn[aria-pressed="true"] { background: #fff; color: var(--ink); border-color: #fff; }

/* One button, two faces: a triangle while the show is off, two bars while it runs. */
.viewer-play .play-off { display: none; }
.viewer-play[aria-pressed="true"] .play-on { display: none; }
.viewer-play[aria-pressed="true"] .play-off { display: block; }

/* On a phone the row of actions runs wider than the screen once every button carries a word,
   so the words go and the icons stay. Every button keeps an aria-label for screen readers. */
@media (max-width: 719px) {
    .viewer-actions .btn-label { display: none; }

    .viewer-actions .btn {
        width: 44px;
        height: 44px;
        min-height: 44px;
        padding: 0;
        border-radius: 50%;
        justify-content: center;
    }

    /* Icons drawn at fifteen pixels read fine beside a word, but look like specks once they
       stand alone inside a circle, so they grow with the words gone. */
    .viewer-actions .btn svg { width: 22px; height: 22px; }

    /* The heart carries its count, so it stays a pill rather than a circle. */
    .viewer-actions #viewerLike { width: auto; padding: 0 14px; border-radius: 999px; }
}
.viewer-actions .btn.liked { background: var(--like); border-color: var(--like); color: #fff; }

.burst-rail { display: flex; gap: 6px; overflow-x: auto; padding: 0 0 12px; }
.burst-rail[hidden] { display: none; }
.burst-rail button { flex: 0 0 auto; width: 56px; height: 56px; border-radius: 8px; overflow: hidden; border: 2px solid transparent; background: rgba(255, 255, 255, .1); cursor: pointer; padding: 0; }
.burst-rail button img { width: 100%; height: 100%; object-fit: cover; }
.burst-rail button[aria-pressed="true"] { border-color: #fff; }

/* --- Overlays ------------------------------------------- */

.overlay {
    position: fixed;
    inset: 0;
    z-index: 70;
    background: rgba(10, 11, 12, .5);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
}

.overlay[hidden] { display: none; }

/* The claim sheet is centred on desktop and slides up from the bottom on phones. */
@media (max-width: 719px) {
    .overlay.bottom { align-items: flex-end; padding: 0; }
    .overlay.bottom .sheet { border-radius: 18px 18px 0 0; }
}

.modal {
    width: 100%;
    max-width: 430px;
    background: var(--sheet);
    border-radius: 18px;
    padding: 26px;
    animation: fade-up .2s ease-out;
    max-height: 92vh;
    overflow-y: auto;
}

.modal h2 { font: 400 23px/1.2 var(--serif); margin: 0 0 18px; }

/* The propose-a-meet-up sheet carries a form with a date field and a place field side by side.
   At 430px the place field is barely wider than its own placeholder, so give this one more room. */
#eventOverlay .modal { max-width: 620px; }
#eventOverlay .event-form { margin-bottom: 0; }
.modal.center { text-align: center; padding: 34px 28px 26px; max-width: 420px; }
.modal.center h2 { font-size: 26px; margin-bottom: 12px; }
.modal.center p { margin: 0 0 26px; font-size: 13.5px; line-height: 1.65; color: var(--muted); }
.modal.center > img { width: 56px; height: 56px; border-radius: 50%; object-fit: cover; margin: 0 auto 18px; }

.sheet {
    width: 100%;
    max-width: 860px;
    max-height: 88vh;
    background: var(--sheet);
    border-radius: 18px;
    display: flex;
    flex-direction: column;
    animation: fade-up .22s ease-out;
}

.sheet-head { flex: 0 0 auto; padding: 22px 22px 14px; border-bottom: 1px solid rgba(18, 22, 26, .09); }
.sheet-head-row { display: flex; align-items: flex-start; gap: 14px; margin-bottom: 16px; }
.sheet-head h2 { font: 400 25px/1.2 var(--serif); margin: 0 0 6px; }
.sheet-head p { margin: 0; font-size: 13px; color: var(--muted); line-height: 1.55; }

.sheet-close {
    border: 0;
    background: rgba(18, 22, 26, .07);
    width: 32px;
    height: 32px;
    border-radius: 50%;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--ink);
    flex: 0 0 auto;
}

.sheet-body { flex: 1 1 auto; overflow-y: auto; padding: 18px 22px 8px; }
.sheet-foot {
    flex: 0 0 auto;
    padding: 16px 22px calc(20px + env(safe-area-inset-bottom));
    border-top: 1px solid rgba(18, 22, 26, .09);
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: nowrap;
}
.sheet-foot span { font-size: 13px; color: var(--muted); flex: 1 1 auto; min-width: 0; }
.sheet-foot .btn { flex: 0 0 auto; white-space: nowrap; }

.field {
    width: 100%;
    border: 1px solid rgba(18, 22, 26, .16);
    background: #fff;
    border-radius: 10px;
    padding: 12px 14px;
    font-size: 14px;
    font-family: inherit;
    color: var(--ink);
}

.field:focus { outline: 2px solid rgba(18, 58, 107, .25); outline-offset: 1px; }

/* Date and time boxes carry an intrinsic minimum width on iPhone, wide enough to push the box past
   the edge of the sheet. Only min-width lets them shrink back into the frame. */
input[type="date"].field,
input[type="datetime-local"].field,
input[type="time"].field {
    min-width: 0;
    width: 100%;
    max-width: 100%;
    /* Safari lays these out as inline-flex, and then a width of 100% still leaves the box short of
       the column. Block layout makes it fill the row like every other field. */
    display: block;
    -webkit-appearance: none;
    appearance: none;
    /* Empty, these boxes have no text to give them height on iPhone and shrink to a thin strip,
       so the row is pinned to the same height as every other field. Left aligned too: Safari
       centres the value once it is filled in. */
    box-sizing: border-box;
    height: 46px;
    padding: 0 14px;
    line-height: 44px;
    text-align: left;
}

/* iPhone zooms the whole page when tapping an input with a font size under 16px, and the zoomed
   sheet then spills past the right edge. Exactly 16px on touch screens stops the zoom. */
@media (pointer: coarse) {
    .field,
    input[type="text"],
    input[type="search"],
    input[type="email"],
    input[type="tel"],
    input[type="date"],
    input[type="datetime-local"],
    select,
    textarea { font-size: 16px; }
}

/* --- Face picker grid --- */

/* Eight cells per row so faces fill the frame; auto-fill spawns dozens of tiny cells on wide screens. */
.face-grid { display: grid; grid-template-columns: repeat(8, minmax(0, 1fr)); gap: 12px; }

@media (max-width: 1080px) { .face-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); } }
@media (max-width: 820px) { .face-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 560px) { .face-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* The "who are you" sheet is locked to EIGHT people per row, so many faces fit in one glance.
   Narrow phones drop columns rather than shrinking the faces. */
#identityGrid { grid-template-columns: repeat(8, 1fr); }

@media (max-width: 860px) {
    #identityGrid { grid-template-columns: repeat(5, 1fr); }
}

@media (max-width: 560px) {
    #identityGrid { grid-template-columns: repeat(3, 1fr); }
}

.face-card { display: block; cursor: pointer; text-align: center; background: none; border: 0; padding: 0; font-family: inherit; color: inherit; text-decoration: none; }
.face-card .photo {
    aspect-ratio: 1;
    border-radius: 12px;
    overflow: hidden;
    background: rgba(18, 22, 26, .07);
    margin-bottom: 7px;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 2px solid transparent;
    transition: border-color .18s;
}
.face-card .photo img { width: 100%; height: 100%; object-fit: cover; }
.face-card:hover .photo { border-color: rgba(18, 58, 107, .4); }
.face-card[aria-pressed="true"] .photo { border-color: var(--accent); }
.face-card .name { font-size: 12px; font-weight: 500; line-height: 1.3; }
.face-card .sub { font-size: 11px; color: var(--muted); margin-top: 2px; }
.face-card.claimed { opacity: .45; cursor: not-allowed; }

.claim-tick {
    position: absolute;
    top: 6px;
    right: 6px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: var(--accent);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* --- Share box ------------------------------------------ */

.share-preview { background: #fff; border: 1px solid rgba(18, 22, 26, .12); border-radius: 12px; overflow: hidden; margin-bottom: 12px; }
.share-preview img { width: 100%; height: auto; aspect-ratio: 1200 / 630; object-fit: cover; }
.account-waiting {
    font-size: 12.5px;
    line-height: 1.55;
    color: #7A5A16;
    background: rgba(190, 143, 42, .1);
    border: 1px solid rgba(190, 143, 42, .28);
    border-radius: 10px;
    padding: 9px 11px;
    margin: 10px 0 2px;
}

.dl-stop {
    flex: 0 0 auto;
    border: 1px solid rgba(18, 22, 26, .2);
    background: transparent;
    color: var(--ink);
    font-size: 12.5px;
    min-height: 32px;
    padding: 0 14px;
    border-radius: 999px;
    margin-left: 10px;
    white-space: nowrap;
}

.share-tones { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 14px; }
.share-caption { width: 100%; min-height: 132px; resize: vertical; line-height: 1.6; }
.share-row { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 16px; }
.share-row .btn { flex: 1 1 120px; }
.share-hint { font-size: 12px; color: var(--muted); margin-top: 12px; line-height: 1.5; text-align: left; -webkit-hyphens: none; hyphens: none; }

.empty { font-size: 13.5px; color: var(--muted); padding: 40px 0; text-align: center; }
/* Inside a photo grid the empty line is a grid item, so it sat squeezed in the first column
   instead of centred across the page. The masonry grid flows in columns, so the line also has
   to span every column or half the sentence lands in the next one. */
.photo-grid .empty, .face-grid .empty { grid-column: 1 / -1; column-span: all; width: 100%; }

/* The single photo page: the whole page keeps the site width instead of running edge to edge. */
.single-wrap { max-width: 1280px; margin: 0 auto; }

/* An album with no shots yet: the invitation stands on its own, centred on the page. */
.empty-note {
    max-width: 520px;
    margin: 0 auto;
    padding: clamp(48px, 9vw, 96px) 24px clamp(60px, 11vw, 110px);
    text-align: center;
    color: var(--muted);
}
.empty-note p { margin: 0 0 10px; font-size: 14.5px; line-height: 1.7; }
.empty-note p + p { color: var(--muted); }
.empty-note .btn { margin-top: 18px; }

/* Album pages keep the same gutter as their heading. The all photos grid stays edge to edge, but
   an album often holds a handful of shots and they read as misaligned under the title. */
#albumGrid {
    max-width: 1280px;
    margin: 0 auto;
    padding: 0 clamp(16px, 4vw, 40px);
}

/* This grid is dealt into columns by script too, see the note on the personal grids. */
#albumGrid.by-cols {
    column-count: initial;
    display: flex;
    align-items: flex-start;
    gap: clamp(10px, 1.2vw, 18px);
}

/* The trailing gap has to be a real flex item, not right padding. A scrolling flex row leaves its
   right padding out of the scroll width, so the last tab stayed clipped even at the end of the
   scroll: the shared moments tab lost its last letters. */
.subtabs-inner::after {
    content: '';
    flex: 0 0 clamp(16px, 4vw, 40px);
}

/* Six per row on a computer: a wide screen otherwise cuts an album into nine tiny stamps. The all
   photos grid keeps its own count, that one is meant to read as a mosaic. */
@media (min-width: 900px) {
    #albumGrid { --photo-cols: 6; }
}

/* Some panels wait on the server for a few seconds: show that the work is running. */
/* While the caption is being written the box itself says so, with a soft pulse on the hint text. */
.share-caption.is-writing { color: var(--muted); }
.share-caption.is-writing::placeholder { color: #123A6B; opacity: 1; animation: writing-pulse 1.1s ease-in-out infinite; }
@keyframes writing-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
@media (prefers-reduced-motion: reduce) { .share-caption.is-writing::placeholder { animation: none; } }

.loading-note { padding: 40px 16px; font-size: 13.5px; line-height: 1.6; color: var(--muted); text-align: center; }
.photo-grid .loading-note, .face-grid .loading-note { grid-column: 1 / -1; }

.loading-dot {
    display: inline-block;
    vertical-align: -4px;
    margin-right: 9px;
    box-sizing: border-box;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    border: 2px solid rgba(18, 22, 26, .14);
    border-top-color: #123A6B;
    animation: spin-loading .8s linear infinite;
}

@keyframes spin-loading { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .loading-dot { animation-duration: 2.4s; } }

/* A person's name that opens their album: same colour as the text around it, underlined only on
   hover, so a card full of names does not turn into a wall of blue. */
.name-link { color: inherit; text-decoration: none; border-bottom: 1px solid rgba(18, 58, 107, .28); }
.name-link:hover { color: var(--accent); border-bottom-color: currentColor; }

/* Bottom breathing room for every tab, otherwise the last row of cards touches the
   screen edge. The mobile tab bar sits on top of this, so keep it clear of that too. */
/* Every block on the reunion page shares the same 1280 frame as the banner at the top, so the
   picture wall, the rails and the banner all line up on one pair of edges. */
section[data-panel] { padding-bottom: 70px; max-width: 1280px; margin: 0 auto; }

/* The wall used to run to the screen edge on a 10px pad. Inside the frame it takes the same
   gutter as everything else, otherwise its edge sits 30px inside the banner above it. */
section[data-panel] > .photo-grid { padding-left: clamp(16px, 4vw, 40px); padding-right: clamp(16px, 4vw, 40px); }

/* --- Profile page --- */

.profile { padding-bottom: 70px; }

/* Same face grid as the roll call tab, so the same column counts, kept inside the 1280px band:
   full bleed would give thirteen columns on a wide screen and shrink every face. */
/* Now three groups stacked, so the outer box only holds them apart. */
.claim-grid {
    display: flex;
    flex-direction: column;
    gap: 34px;
    padding: 0 clamp(16px, 4vw, 40px);
    max-width: 1280px;
    margin: 0 auto;
}

.claim-group-head {
    display: flex;
    align-items: baseline;
    gap: 10px;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: #5F6469;
    padding-bottom: 10px;
    margin-bottom: 16px;
    border-bottom: 1px solid rgba(18, 22, 26, .10);
}

.claim-group-head span { letter-spacing: 0; text-transform: none; font-weight: 400; color: #9A9EA3; }

.claim-group-grid {
    display: grid;
    grid-template-columns: repeat(11, minmax(0, 1fr));
    gap: 12px;
}

/* No photo in the album: a letter stands in, so every cell keeps the same shape. */
.face-card .photo .letter { font: 400 22px/1 var(--serif); color: rgba(18, 22, 26, .4); }

/* Somebody already signed in as this person. The cell stays in place so the roster reads whole. */
.face-card.is-taken { opacity: .4; cursor: not-allowed; }

@media (max-width: 1180px) {
    .claim-group-grid { grid-template-columns: repeat(8, minmax(0, 1fr)); }
}

@media (max-width: 900px) {
    .claim-group-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}

@media (max-width: 560px) {
    .claim-group-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.claim-grid .face-card { border: 0; background: transparent; padding: 0; cursor: pointer; text-align: left; font-family: inherit; }
.claim-grid .face-card:disabled { opacity: .5; }

.profile-cover {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 20px;
    padding: clamp(24px, 4vw, 44px) clamp(16px, 4vw, 40px);
}

.profile-id { display: flex; align-items: center; gap: 18px; flex: 1 1 300px; }

.profile-avatar {
    width: 84px;
    height: 84px;
    border-radius: 50%;
    overflow: hidden;
    background: rgba(18, 22, 26, .07);
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
}

.profile-avatar img { width: 100%; height: 100%; object-fit: cover; }
.profile-avatar span { font: 400 30px/1 var(--serif); color: rgba(18, 22, 26, .3); }
.profile-cover h1 { font: 400 clamp(26px, 3vw, 38px)/1.15 var(--serif); margin: 4px 0; }
.profile-cover p { margin: 0; font-size: 13.5px; color: var(--muted); }

/* The scroller spans the whole band so the inner layer can centre itself, same trick as the sub-tabs.
   Do not use justify-content: center, an overflow would clip the first tab. */
.profile-tabs {
    overflow-x: auto;
    /* The underline sits 1px past the box, and that single pixel makes the strip vertically
       scrollable on iOS: a diagonal swipe then drags the whole row up and down. */
    overflow-y: hidden;
    overscroll-behavior: contain;
    scrollbar-width: none;
    max-width: 1280px;
    margin: 24px auto 0;
    border-bottom: 1px solid rgba(18, 22, 26, .10);
}

.profile-tabs::-webkit-scrollbar { display: none; }

.profile-tabs-inner {
    display: flex;
    gap: 6px;
    width: max-content;
    max-width: 100%;
    margin: 0 auto;
    padding: 0 clamp(16px, 4vw, 40px);
}

/* Same family as the reunion sub-tabs: plain underlined text, not pills.
   The negative margin-bottom lets the underline sit on the bar rule instead of doubling it. */
.profile-tabs-inner button {
    display: inline-flex;
    align-items: center;
    flex: 0 0 auto;
    border: 0;
    border-bottom: 2px solid transparent;
    background: transparent;
    color: #5F6469;
    font-size: 13.5px;
    font-weight: 400;
    min-height: 46px;
    padding: 0 12px;
    margin-bottom: -1px;
    cursor: pointer;
    white-space: nowrap;
}

.profile-tabs-inner button[aria-pressed="true"] { border-bottom-color: var(--accent); color: var(--accent); font-weight: 600; }

/* An unopened tab dims its badge too, otherwise the row is all dark blue dots. */
.profile-tabs-inner button:not([aria-pressed="true"]) .tab-dot { background: rgba(18, 22, 26, .08); color: #5F6469; }

/* Every block on the profile page shares one gutter and never touches the screen edge. The profile
   is NOT part of the reunion tabs, so its photo grid stays inside the 1280px band. */
[data-mine-panel] { padding: 18px clamp(16px, 4vw, 40px) 60px; max-width: 1280px; margin: 0 auto; }
/* Both grids inside the profile wear the same masonry as the shared moments page: five columns
   inside the 1280 frame, thinning down on narrow screens. */
[data-mine-panel] > .photo-grid {
    column-count: 5;
    column-gap: clamp(10px, 1.2vw, 18px);
    max-width: 1280px;
    margin: 10px auto 0;
    padding: 0;
}

@media (max-width: 899px) { [data-mine-panel] > .photo-grid { column-count: 4; } }
@media (max-width: 719px) { [data-mine-panel] > .photo-grid { column-count: 3; } }
@media (max-width: 519px) { [data-mine-panel] > .photo-grid { column-count: 2; } }

/* The personal grids are dealt into columns by script, because the built-in column layout leaves
   columns empty when there are few pictures. Each column is a block of equal width. */
[data-mine-panel] > .photo-grid.by-cols {
    column-count: initial;
    display: flex;
    align-items: flex-start;
    gap: clamp(10px, 1.2vw, 18px);
}
.grid-col { flex: 1 1 0; min-width: 0; }

/* My photos follows the shared moments layout: five columns inside the 1280 frame rather than
   edge to edge. The wall on the all photos tab keeps its own look, the two serve different
   purposes: one is the whole wall of the class, the other one person's own album. */
#mineWrap > .photo-grid {
    column-count: 5;
    column-gap: clamp(10px, 1.2vw, 18px);
    max-width: 1280px;
    margin: 0 auto;
    padding: 0 clamp(16px, 4vw, 40px) 40px;
}

/* Five columns are held all the way down to 900px: a laptop window of ordinary size plus a
   scrollbar falls under 1099, and the row would drop to four. */
@media (max-width: 899px) { #mineWrap > .photo-grid { column-count: 4; } }
@media (max-width: 719px) { #mineWrap > .photo-grid { column-count: 3; } }
@media (max-width: 519px) { #mineWrap > .photo-grid { column-count: 2; } }

/* The lead-in runs the full width of the frame and is centred: at 760px it wrapped while both
   sides sat empty. */
.profile-note { margin: 0 0 18px; font-size: 13.5px; color: var(--muted); line-height: 1.6; text-align: center; }

/* Profile page on phones: tabs scroll sideways, buttons stay big enough for a thumb. */
@media (max-width: 640px) {
    .profile-cover { flex-direction: column; align-items: flex-start; gap: 14px; }
    /* In a column, flex-basis becomes height: 300px would make the name block 300px tall with the
       avatar and text floating in empty space. A column only needs to be as tall as its content. */
    .profile-id { gap: 14px; flex: 0 0 auto; width: 100%; }
    .profile-avatar { width: 64px; height: 64px; }
    .profile-cover h1 { font-size: 26px; }
    .page-head-actions { width: 100%; }
    .page-head-actions .btn { flex: 1 1 auto; justify-content: center; }
    .leader-row { flex-wrap: wrap; }
    .leader-row .acts { width: 100%; }
    .leader-row .acts .btn { flex: 1 1 auto; }
    .send-box { padding: 16px; }
}

.field-label { display: block; font-size: 12.5px; color: var(--muted); margin: 14px 0 6px; }

.avatar-picker { display: grid; grid-template-columns: repeat(6, 1fr); gap: 8px; margin-bottom: 6px; }

.avatar-pick {
    aspect-ratio: 1;
    border: 2px solid transparent;
    border-radius: 50%;
    overflow: hidden;
    padding: 0;
    background: rgba(18, 22, 26, .07);
    cursor: pointer;
}

.avatar-pick img { width: 100%; height: 100%; object-fit: cover; }
.avatar-pick[aria-pressed="true"] { border-color: var(--accent); }

/* Fix-my-photos grid: cells are wider than the picker because each one carries a button. */
/* Eight per row, filling the frame: the panel already has its own gutter, adding another one
   would indent the grid compared with the lead-in right above it. */
#profileFaces { grid-template-columns: repeat(8, minmax(0, 1fr)); padding: 0; }

@media (max-width: 1080px) { #profileFaces { grid-template-columns: repeat(6, minmax(0, 1fr)); } }
@media (max-width: 820px) { #profileFaces { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 560px) { #profileFaces { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
#profileFaces .face-card { display: flex; flex-direction: column; gap: 8px; }
#profileFaces .face-card .photo { display: block; margin-bottom: 0; }
#profileFaces .face-card .btn { width: 100%; white-space: normal; }

.switch { display: flex; align-items: center; gap: 10px; margin-top: 16px; font-size: 13.5px; cursor: pointer; }
.switch input { width: 18px; height: 18px; accent-color: var(--accent); }

/* --- Phones --- */

@media (max-width: 640px) {
    .topbar-inner { gap: 8px; padding: 8px 16px; }
    .viewer-nav { display: none; }
    .modal { max-width: 100%; }
}

/* Below 880px the tabs move to a fixed bottom bar and the top bar keeps only the logo and two
   buttons. Measured: the row itself is 389px and the school name needs 198px on each outer
   column, so the tab row cannot stay on the bar under about 880px without trimming the name. */

.tabbar { display: none; }
.btn-select .icon-only { display: none; }
.btn-login .icon-only { display: none; }

/* The button only works on tabs that have a photo grid, but hiding it outright makes the right
   cluster jump on every tab change. Keep its slot and just make it invisible and unclickable. */
.btn-select[hidden] { display: inline-flex; visibility: hidden; pointer-events: none; }

@media (max-width: 879px) {
    .topbar-inner .tabs { display: none; }

    /* The tab row is gone at this width, so the bar has only two blocks left. Keeping three
       columns parks the account cluster in the middle one, which reads as the avatar drifting
       to the centre of the screen and sitting on top of the school name. */
    .topbar-inner { grid-template-columns: minmax(0, 1fr) auto; }
    .topbar-right { justify-self: end; }

    /* The name beside the round face is what eats the width the school name needs. The face
       alone still opens the same filter, and the name is one tap away in the account menu. */
    .me-chip { width: 44px; padding: 0; justify-content: center; }

    /* One fixed height for every page. The upload button only exists on photo pages, so without
       this the bar on those pages ends up a couple of pixels taller than on the home page. */
    .topbar-inner { min-height: 60px; }
    .topbar-right > .btn,
    .topbar-right > .account,
    .topbar-right > .me-chip { height: 44px; }

    .btn-select {
        width: 44px;
        height: 44px;
        padding: 0;
        border-radius: 50%;
        justify-content: center;
    }

    .btn-select .icon-only { display: block; }
    .btn-select .btn-select-label { display: none; }

    /* Same treatment for the sign-in button: the words take the room the school name needs, and
       the round face is what the bar shows once someone is signed in anyway. */
    .btn-login {
        width: 44px;
        height: 44px;
        min-height: 44px;
        padding: 0;
        border-radius: 50%;
        justify-content: center;
    }

    .btn-login .icon-only { display: block; }
    .btn-login .btn-login-label { display: none; }
    .me-chip { min-height: 44px; }

    body { padding-bottom: calc(74px + env(safe-area-inset-bottom)); }

    .tabbar {
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 44;
        display: grid;
        grid-template-columns: repeat(5, 1fr);
        background: rgba(242, 240, 236, .94);
        backdrop-filter: blur(14px);
        -webkit-backdrop-filter: blur(14px);
        border-top: 1px solid var(--line);
        padding-bottom: env(safe-area-inset-bottom);
    }

    .tabbar button,
    .tabbar a {
        position: relative;
        border: 0;
        text-decoration: none;
        background: transparent;
        color: #5F6469;
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 5px;
        padding: 11px 4px 10px;
        cursor: pointer;
        font-family: inherit;
        font-size: 11.5px;
        font-weight: 400;
        letter-spacing: .01em;
        min-height: 44px;
    }

    /* Lock the size for BOTH the button and the links: without the `a` rule the other four icons
       grow with their cell, up to 70px, and their labels fall out of line. */
    .tabbar button svg,
    .tabbar a svg { width: 21px; height: 21px; flex: 0 0 auto; stroke-width: 1.4; }
    .tabbar button[aria-pressed="true"],
    .tabbar a[aria-current="page"] { color: var(--accent); font-weight: 600; }
    .tabbar button[aria-pressed="true"] svg,
    .tabbar a[aria-current="page"] svg { stroke-width: 1.9; }

    /* While selecting photos the floating bar covers the tab bar. */
    body.selecting .tabbar { display: none; }
}


/* --- Album share box ------------------------------------ */

.album-share {
    width: 100%;
    max-width: 420px;
    background: #F7F6F3;
    border-radius: 18px;
    padding: 26px 24px 22px;
    animation: fadeUp .2s ease-out;
}

.album-share .eyebrow { margin-bottom: 8px; letter-spacing: .16em; color: #5F6469; }
.album-share h2 { font: 400 24px/1.25 var(--serif); margin: 0 0 18px; }

.album-share-link {
    display: flex;
    align-items: center;
    gap: 8px;
    background: #fff;
    border: 1px solid rgba(18, 22, 26, .12);
    border-radius: 12px;
    padding: 11px 12px;
}

.album-share-link input {
    flex: 1 1 auto;
    min-width: 0;
    border: 0;
    background: none;
    font: 400 12.5px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace;
    color: var(--ink);
    text-overflow: ellipsis;
    outline: none;
}

.album-share-link button {
    flex: 0 0 auto;
    height: 34px;
    padding: 0 14px;
    border: 0;
    border-radius: 8px;
    background: #12161A;
    color: #fff;
    font-size: 12.5px;
    cursor: pointer;
    white-space: nowrap;
}

.album-share-row { display: flex; gap: 10px; margin-top: 14px; }

.album-share-row button {
    flex: 1;
    height: 46px;
    border: 0;
    border-radius: 999px;
    color: #fff;
    font-size: 13.5px;
    font-weight: 600;
    cursor: pointer;
}

.album-share-fb { background: #1877F2; }

/* Phones hide the Facebook button: its app grabs every facebook.com link and then does nothing
   with the share page, so the built in share sheet is the only route that works there. */
@media (hover: none) and (pointer: coarse) {
    .share-desktop-only { display: none; }
    .album-share-row:not(:has(button:not(.share-desktop-only))) { display: none; }
}

.album-share-native { width: 100%; margin-top: 14px; min-height: 46px; }
/* Justified: in a narrow sheet this note wraps and its ragged right edge left the box looking
   half empty. */
.album-share-note { font-size: 12px; color: #5F6469; margin: 16px 0 6px; line-height: 1.5; text-align: left; -webkit-hyphens: none; hyphens: none; }
.album-share-close { width: 100%; min-height: 44px; }

@keyframes fadeUp {
    from { opacity: 0; transform: translateY(10px); }
    to { opacity: 1; transform: none; }
}


/* Two fields side by side in the edit sheet, stacked on phones. */
.field-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 0 14px; }

/* Grid children keep an automatic minimum width, so a date box with a wide built-in size pushes
   its column open and spills out of the sheet. */
.field-pair > * { min-width: 0; }

/* On a phone the two columns are too narrow for a date box anyway: stack them. */
@media (max-width: 560px) {
    .field-pair { grid-template-columns: 1fr; }
}

@media (max-width: 520px) {
    .field-pair { grid-template-columns: minmax(0, 1fr); }
}


/* Waiting-for-approval label, shown in place of the edit profile button. */
.waiting-badge {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    min-height: 44px;
    padding: 0 16px;
    border-radius: 999px;
    background: rgba(190, 143, 42, .14);
    color: #7A5A16;
    font-size: 13px;
    font-weight: 500;
}


/* --- Class manager tab --- */

/* Intro band at the top of the tab, spelling out what can be done right here. */
.leader-intro {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 14px;
    background: #10305B;
    color: #fff;
    border-radius: 16px;
    padding: 18px 22px;
    margin-bottom: 18px;
}

.leader-tag {
    font-size: 11px;
    letter-spacing: .14em;
    text-transform: uppercase;
    padding: 5px 12px;
    border-radius: 999px;
    background: rgba(255, 255, 255, .16);
    flex: 0 0 auto;
}

.leader-intro p { margin: 0; font-size: 13.5px; line-height: 1.6; flex: 1 1 260px; color: rgba(255, 255, 255, .88); }

.leader-nums {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 170px), 1fr));
    gap: 10px;
    margin-bottom: 28px;
}

.leader-nums > div {
    background: var(--card);
    border: 1px solid rgba(18, 22, 26, .09);
    border-radius: 14px;
    padding: 16px 18px;
}

.leader-nums b { display: block; font: 400 30px/1 var(--serif); font-weight: 400; }
.leader-nums span { display: block; font-size: 12px; color: #5F6469; margin-top: 6px; }
.leader-nums .warn b { color: #B23A2F; }

/* Each task is a white card, no more bare headings on the beige field. */
.leader-block {
    background: var(--card);
    border: 1px solid rgba(18, 22, 26, .09);
    border-radius: 18px;
    padding: 22px;
    margin-bottom: 18px;
}

.leader-block h2 { font: 400 21px/1.25 var(--serif); margin: 0 0 8px; }
.leader-block > p { margin: 0; font-size: 13.5px; line-height: 1.65; color: #4A4F54; }

.leader-list { display: grid; gap: 10px; margin-top: 16px; }
.leader-list.wide { grid-template-columns: repeat(auto-fill, minmax(min(100%, 440px), 1fr)); }
.leader-list.mid { grid-template-columns: repeat(auto-fill, minmax(min(100%, 400px), 1fr)); }
.leader-list.narrow { grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr)); gap: 12px; }
.leader-list.names { grid-template-columns: repeat(auto-fill, minmax(min(100%, 520px), 1fr)); gap: 12px; }

/* A round badge on the tab shows pending work without opening every card. */
.tab-dot {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 20px;
    margin-left: 8px;
    padding: 0 6px;
    border-radius: 999px;
    background: #123A6B;
    color: #fff;
    font-size: 11px;
    font-style: normal;
    font-weight: 500;
}

/* Portrait grid for the subject teachers inside the class manager tab: a face, the name, the
   subject and one button to upload a picture. */
/* Twenty-two teachers never divide evenly into a fixed column count, so the last row would always
   end with a gap on the right. Letting the cells stretch fills every row edge to edge. */
.teacher-avatar-grid { display: flex; flex-wrap: wrap; gap: 12px; }
.teacher-avatar-grid > * { flex: 1 1 170px; }

@media (max-width: 480px) { .teacher-avatar-grid > * { flex: 1 1 130px; } }

.teacher-avatar-cell {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 4px;
    background: var(--card);
    border: 1px solid rgba(18, 22, 26, .09);
    border-radius: 14px;
    padding: 14px 10px 12px;
}

.teacher-avatar-cell .face {
    width: 64px;
    height: 64px;
    border-radius: 50%;
    overflow: hidden;
    background: rgba(18, 22, 26, .07);
    display: flex;
    align-items: center;
    justify-content: center;
    font: 400 21px/1 var(--serif);
    color: rgba(18, 22, 26, .42);
    margin-bottom: 7px;
}

.teacher-avatar-cell .face img { width: 100%; height: 100%; object-fit: cover; }
.teacher-avatar-cell .name { font-size: 13px; font-weight: 500; line-height: 1.35; }
.teacher-avatar-cell .subject { font-size: 12px; color: #5F6469; }

.teacher-avatar-cell .pick {
    margin-top: 8px;
    min-height: 34px;
    display: inline-flex;
    align-items: center;
    padding: 0 14px;
    border: 1px solid rgba(18, 22, 26, .16);
    border-radius: 999px;
    font-size: 12.5px;
    color: var(--accent);
    cursor: pointer;
}

.teacher-avatar-cell .drop {
    margin-top: 6px;
    border: 0;
    background: transparent;
    padding: 0;
    font-size: 12px;
    color: #5F6469;
    text-decoration: underline;
    cursor: pointer;
}

.leader-row {
    display: flex;
    align-items: center;
    gap: 12px;
    background: #F7F6F3;
    border: 1px solid rgba(18, 22, 26, .07);
    border-radius: 14px;
    padding: 12px 14px;
}

.leader-row .face { width: 46px; height: 46px; border-radius: 12px; overflow: hidden; background: rgba(18, 22, 26, .07); flex: 0 0 auto; }
.leader-row .face.big { width: 62px; height: 62px; }
.leader-row .face img { width: 100%; height: 100%; object-fit: cover; }
.leader-row .body { min-width: 0; flex: 1 1 auto; }
/* One line each, cut with an ellipsis when too long: a name breaking across two lines makes the
   row look broken, and the row already has a fixed height next to the 46px face. */
.leader-row .body b { display: block; font-size: 13.5px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.leader-row .body span { display: block; font-size: 12px; color: #5F6469; margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.leader-row .acts { display: flex; gap: 8px; flex: 0 0 auto; flex-wrap: wrap; }
.leader-row .kind { font-size: 11px; padding: 3px 9px; border-radius: 999px; background: rgba(18, 58, 107, .1); color: #123A6B; }
.leader-name-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.leader-name-head span:last-child { font-size: 12px; color: #5F6469; }
.leader-row .body .field { width: 100%; }
.leader-row .body .field + .field { margin-top: 8px; }

/* Class roster the manager fills in for the others. A grid of tap targets, each one person: the
   face first, because the manager recognises the face long before they read the name. */
#rosterSearch { margin-bottom: 14px; }

.roster-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(232px, 1fr));
    gap: 10px;
}

.roster-row {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    text-align: left;
    background: #F7F6F3;
    border: 1px solid rgba(18, 22, 26, .07);
    border-radius: 14px;
    padding: 10px 12px;
    cursor: pointer;
    min-height: 62px;
}

.roster-row:hover { border-color: rgba(18, 58, 107, .3); background: #fff; }
.roster-row .face { width: 42px; height: 42px; border-radius: 12px; overflow: hidden; background: rgba(18, 22, 26, .07); flex: 0 0 auto; display: flex; align-items: center; justify-content: center; font-size: 15px; color: #5F6469; }
.roster-row .face img { width: 100%; height: 100%; object-fit: cover; }
.roster-row .body { min-width: 0; flex: 1 1 auto; }
.roster-row .body b { display: block; font-size: 13.5px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.roster-row .body span { display: block; font-size: 12px; color: #5F6469; margin-top: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* A person with nothing filled in yet is the whole reason for this list, so say so plainly.
   The class name stays away from the shared `.empty` rule, which pads empty-state text by 40px. */
.roster-row .body span.missing { color: #A2503C; }
.roster-row .no { font-size: 12px; color: #8A8F94; flex: 0 0 auto; }

/* The face beside the heading of the editing box. */
.roster-who { display: flex; align-items: center; gap: 14px; min-width: 0; }
.roster-who .face { width: 54px; height: 54px; border-radius: 14px; overflow: hidden; background: rgba(18, 22, 26, .07); flex: 0 0 auto; display: flex; align-items: center; justify-content: center; color: #5F6469; }
.roster-who .face img { width: 100%; height: 100%; object-fit: cover; }

#rosterOverlay .sheet { max-width: 560px; }
#rosterOverlay .sheet-foot { flex-wrap: wrap; }
#rosterOverlay .sheet-foot .share-hint { margin: 0; flex: 1 1 100%; }

/* Portrait of a subject teacher, uploaded from inside the editing box. */
.roster-avatar { display: flex; align-items: center; gap: 14px; margin-bottom: 8px; }
.roster-avatar .face {
    width: 62px;
    height: 62px;
    border-radius: 14px;
    overflow: hidden;
    background: rgba(18, 22, 26, .07);
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #5F6469;
}
.roster-avatar .face img { width: 100%; height: 100%; object-fit: cover; }
.roster-avatar-acts { display: flex; flex-wrap: wrap; gap: 8px; }

/* A heading takes the whole row of the grid, so a group always starts on a fresh line. */
.roster-group {
    grid-column: 1/-1;
    font-size: 11px;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: #5F6469;
    margin: 10px 0 2px;
}

.roster-group:first-child { margin-top: 0; }

@media (max-width: 519px) {
    .roster-list { grid-template-columns: 1fr; }
}

/* Every empty state is a centred dashed frame inside the card, never text stuck in a corner. */
.leader-empty {
    grid-column: 1/-1;
    text-align: center;
    padding: 34px 20px;
    border: 1px dashed rgba(18, 22, 26, .18);
    border-radius: 14px;
    font-size: 13.5px;
    color: #5F6469;
}

.leader-shots { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 168px), 1fr)); gap: 14px; margin-top: 16px; }

/* Head of a block: the title on the left, a batch button on the right. */
.leader-block-head { display: flex; align-items: flex-start; gap: 14px; flex-wrap: wrap; }
.leader-block-head > div { flex: 1 1 260px; min-width: 0; }
.leader-block-head .btn { flex: 0 0 auto; }

/* Phones: two per row. One per row means scrolling forever through a batch of twenty. */
@media (max-width: 560px) {
    .leader-shots { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
    .leader-shot .acts { flex-wrap: wrap; }
    .leader-shot .acts .btn { flex: 1 1 auto; }
}
.leader-shot .shot { position: relative; aspect-ratio: 1; border-radius: 12px; overflow: hidden; background: rgba(18, 22, 26, .06); }
.leader-shot .shot img { width: 100%; height: 100%; object-fit: cover; }

.leader-shot .album {
    position: absolute;
    left: 8px;
    top: 8px;
    font-size: 11px;
    padding: 4px 9px;
    border-radius: 999px;
    background: rgba(255, 255, 255, .92);
    color: #123A6B;
}

.leader-shot .sender { font-size: 12px; color: #5F6469; margin: 8px 0 8px; }
.leader-shot .acts { display: flex; gap: 8px; }
.leader-shot .acts .btn { flex: 1 1 auto; justify-content: center; }

.leader-faces {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 150px), 1fr));
    gap: 14px;
    margin-top: 16px;
}

.leader-face .face { position: relative; aspect-ratio: 1; border-radius: 12px; overflow: hidden; background: rgba(18, 22, 26, .07); }
.leader-face .face img { width: 100%; height: 100%; object-fit: cover; }

.leader-face .sub {
    position: absolute;
    right: 8px;
    bottom: 8px;
    font-size: 11px;
    padding: 3px 8px;
    border-radius: 999px;
    background: rgba(255, 255, 255, .92);
    color: #5F6469;
}

.leader-face select { width: 100%; font-size: 12.5px; padding: 9px 14px; border-radius: 999px; margin-top: 8px; }


/* --- Uploading your own photos --- */

.send-cols { display: grid; grid-template-columns: minmax(0, 420px) minmax(0, 1fr); gap: 20px; align-items: start; }

@media (max-width: 760px) {
    .send-cols { grid-template-columns: minmax(0, 1fr); }
}

.send-box {
    background: var(--card);
    border: 1px solid rgba(18, 22, 26, .09);
    border-radius: 18px;
    padding: 22px;
}

.send-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 52px;
    height: 52px;
    border-radius: 15px;
    background: rgba(18, 58, 107, .07);
    color: #123A6B;
    margin-bottom: 16px;
}

.send-icon svg { width: 24px; height: 24px; stroke-width: 1.5; }
.send-box h2 { font: 400 clamp(21px, 2vw, 25px)/1.25 var(--serif); margin: 0 0 10px; }
.send-box p { margin: 0 0 18px; font-size: 14px; line-height: 1.65; color: #4A4F54; }
.send-box .field-label { display: block; font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: #5F6469; margin-bottom: 10px; }

/* The upload target is a drop down: a class can gather hundreds of meet-ups and a stack of radio
   rows would run down the page forever. */
.send-target { margin-bottom: 16px; }

/* Safari ignores padding on a select until the native look is switched off, so these boxes came
   out half the height of every other field. The arrow is drawn back in as a background. */
select.field {
    -webkit-appearance: none;
    appearance: none;
    box-sizing: border-box;
    height: 46px;
    padding: 0 38px 0 14px;
    line-height: 44px;
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none' stroke='%235F6469' stroke-width='1.6'%3E%3Cpath d='M1 1.5 6 6.5l5-5'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 14px center;
}

.send-kinds { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; }

/* A chip instead of a bare radio: the whole row is clickable and a thumb cannot miss it. */
.send-kind {
    display: flex;
    align-items: center;
    gap: 11px;
    min-height: 46px;
    padding: 0 14px;
    border: 1px solid rgba(18, 22, 26, .14);
    border-radius: 12px;
    font-size: 13.5px;
    cursor: pointer;
}

.send-kind input { position: absolute; opacity: 0; pointer-events: none; }
.send-kind i { flex: 0 0 auto; width: 17px; height: 17px; border-radius: 50%; border: 1.6px solid rgba(18, 22, 26, .28); position: relative; }
.send-kind.on { border-color: #123A6B; background: rgba(18, 58, 107, .06); }
.send-kind.on span { font-weight: 500; }
.send-kind.on i { border-color: #123A6B; }
.send-kind.on i::after { content: ''; position: absolute; inset: 3px; border-radius: 50%; background: #123A6B; }

.send-pick { width: 100%; min-height: 48px; justify-content: center; }

.send-quota { margin-top: 18px; padding-top: 16px; border-top: 1px solid rgba(18, 22, 26, .09); }
.send-quota-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; font-size: 13px; color: #4A4F54; margin-bottom: 9px; }
.send-quota-row b { font-weight: 500; color: var(--ink); }
.send-bar { height: 4px; border-radius: 999px; background: rgba(18, 22, 26, .09); overflow: hidden; }
.send-bar i { display: block; height: 100%; background: #123A6B; border-radius: 999px; }
.send-note { margin: 10px 0 0; font-size: 12px; line-height: 1.6; color: #5F6469; }

.send-mine h2 { font: 400 21px/1.25 var(--serif); margin: 0 0 14px; }
.send-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 146px), 1fr)); gap: 14px; }
.send-cell .shot { position: relative; aspect-ratio: 1; border-radius: 12px; overflow: hidden; background: rgba(18, 22, 26, .06); }
.send-cell .shot img { width: 100%; height: 100%; object-fit: cover; }

/* Status chip in the top left corner, colour coded so one glance tells which shot is live. */
.send-state {
    position: absolute;
    left: 8px;
    top: 8px;
    font-size: 11px;
    font-weight: 500;
    padding: 4px 9px;
    border-radius: 999px;
    background: rgba(190, 143, 42, .18);
    color: #7A5A16;
}

.send-state.on { background: rgba(18, 58, 107, .12); color: #123A6B; }
.send-cell .album { font-size: 12px; color: #5F6469; margin-top: 8px; }
.send-cell .send-drop { margin-top: 6px; }

.send-empty {
    grid-column: 1/-1;
    text-align: center;
    padding: 40px 20px;
    border: 1px dashed rgba(18, 22, 26, .18);
    border-radius: 16px;
    font-size: 13.5px;
    color: #5F6469;
}


/* --- Phones: readable text, thumb sized buttons --- */
/* Most of the class opens the site on a phone, so the smallest type goes up to 12px and every
   button is at least 40px tall. */

@media (max-width: 640px) {
    .btn, .btn-sm { min-height: 40px; }
    .sortbar button { min-height: 40px; }
    .eyebrow { font-size: 11.5px; }
    .tile-meta { font-size: 12px; }
    .tile-burst { font-size: 11.5px; }
    .strip-head span { font-size: 11.5px; }
    .roll-cell .sub { font-size: 12px; }
    .roll-cell .name { font-size: 13px; }
    .face-card .name { font-size: 12.5px; }
    .face-card .sub { font-size: 12px; }
    .people-card .body span { font-size: 12px; }
    .sortbar button { font-size: 13px; }
    .share-hint, .profile-note { font-size: 13px; }
    .leader-row .body span { font-size: 12px; }
    .leader-face .sub { font-size: 12px; }
    .album-share-link input { font-size: 12px; }
    .album-share-note { font-size: 12.5px; }
    .waiting-badge { font-size: 12.5px; }
}


/* --- Events tab ----------------------------------------- */

.events-wrap { padding: clamp(26px, 4vw, 44px) clamp(16px, 4vw, 40px) 70px; max-width: 1280px; margin: 0 auto; }

/* Proposing a meet-up uses a floating button in the bottom right. The old dashed invitation frame
   took a whole band just to say one sentence, so it is gone. */
/* Birthday button, floating in the bottom right corner, and when the meet-up button is out it
   stands one step ABOVE it rather than beside it. Shown only on somebody's birthday, so the
   corner is empty the other 364 days. */
.birthday-fab {
    position: fixed;
    right: clamp(16px, 2.4vw, 34px);
    bottom: clamp(16px, 2.4vw, 30px);
    transition: bottom .18s;
    z-index: 60;
    display: flex;
    align-items: center;
    gap: 9px;
    border: 0;
    background: #123A6B;
    color: #fff;
    font: 600 14px/1 inherit;
    min-height: 50px;
    padding: 0 20px 0 16px;
    border-radius: 999px;
    box-shadow: 0 14px 32px rgba(10, 20, 35, .26);
    cursor: pointer;
    max-width: min(60vw, 320px);
}
.birthday-fab:hover { background: #16457E; }
.birthday-fab-icon { display: flex; }
.birthday-fab-icon svg { width: 21px; height: 21px; }
.birthday-fab-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The meet-up button is out on the events tab, so the birthday one steps up over it. */
body.has-event-fab .birthday-fab { bottom: calc(clamp(16px, 2.4vw, 30px) + 62px); }

/* How many classmates share today, kept visible on phones where the wording is hidden. */
.birthday-fab-count {
    min-width: 21px;
    height: 21px;
    padding: 0 6px;
    border-radius: 999px;
    background: rgba(255, 255, 255, .22);
    font-size: 12px;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* Wider than the other sheets: this one holds several wishes at once, and at 520px a two line
   wish wrapped into five. Phones keep the shared full width. */
.birthday-modal { max-width: 680px; }
.birthday-modal .birthday-wishes { max-height: 320px; }

@media (max-width: 719px) {
    .birthday-modal .birthday-wishes { max-height: 240px; }
}
.birthday-head { display: flex; align-items: flex-start; gap: 14px; }
.birthday-head .face {
    flex: 0 0 auto;
    width: 66px;
    height: 66px;
    border-radius: 15px;
    overflow: hidden;
    background: rgba(18, 22, 26, .07);
    display: flex;
    align-items: center;
    justify-content: center;
    font: 400 26px/1 var(--serif);
    color: rgba(18, 22, 26, .4);
}
.birthday-head .face img { width: 100%; height: 100%; object-fit: cover; }
.birthday-head .body { flex: 1 1 auto; min-width: 0; }
.birthday-head h2 { font: 400 clamp(21px, 3vw, 26px)/1.2 var(--serif); margin: 3px 0 5px; }
.birthday-head p { margin: 0; font-size: 13.5px; color: var(--muted); line-height: 1.5; }
.birthday-head .eyebrow { display: block; margin-bottom: 0; }

@media (max-width: 719px) {
    /* On a phone the column left of the face is too narrow for the invite line, which broke into
       two ragged rows. The line drops to its own full-width row under the name instead. */
    .birthday-head { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; column-gap: 12px; align-items: center; }
    .birthday-head .body { display: contents; }
    .birthday-head .face { grid-column: 1; grid-row: 1 / span 2; width: 58px; height: 58px; }
    .birthday-head .eyebrow { grid-column: 2; grid-row: 1; align-self: end; }
    .birthday-head h2 { grid-column: 2; grid-row: 2; align-self: start; margin: 2px 0 0; }
    .birthday-head .guest-list-close { grid-column: 3; grid-row: 1 / span 2; }
    .birthday-head p { grid-column: 1 / -1; grid-row: 3; margin-top: 10px; }
}

.birthday-tabs { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.birthday-tabs button[aria-pressed="true"] { border-color: var(--accent); color: var(--accent); }

.birthday-wishes { margin-top: 16px; display: grid; gap: 8px; overflow-y: auto; }
.birthday-wish {
    display: flex;
    align-items: baseline;
    gap: 8px;
    background: rgba(18, 22, 26, .035);
    border-radius: 12px;
    padding: 9px 12px;
}
.birthday-wish b { flex: 0 0 auto; font-size: 13px; font-weight: 500; }
.birthday-wish span { font-size: 13.5px; line-height: 1.5; }
.birthday-drop { margin-left: auto; background: none; border: 0; padding: 0 2px; font: inherit; font-size: 12px; color: var(--muted); cursor: pointer; text-decoration: underline; }
.birthday-empty, .birthday-note { margin: 0; font-size: 13px; color: var(--muted); }

.birthday-write { margin-top: 14px; }
.birthday-body { width: 100%; min-height: 168px; resize: vertical; margin-top: 10px; line-height: 1.55; }
.birthday-row { display: flex; align-items: center; gap: 12px; margin-top: 10px; flex-wrap: wrap; }
.birthday-hint { font-size: 12.5px; color: var(--muted); }

@media (max-width: 879px) {
    /* Above the bottom bar, and no wording: a pill with a name would run half the screen wide. */
    .birthday-fab { bottom: calc(var(--tabbar, 64px) + 14px); padding: 0 14px; justify-content: center; gap: 6px; }
    .birthday-fab-text { display: none; }
    body.has-event-fab .birthday-fab { bottom: calc(var(--tabbar, 64px) + 14px + 62px); }
}

.event-fab {
    position: fixed;
    right: clamp(16px, 2.4vw, 34px);
    bottom: clamp(16px, 2.4vw, 30px);
    z-index: 60;
    border: 0;
    /* Deep ink, not the house navy: scrolling past a meet-up card put this button right on top of
       the navy date block and the two merged into one shape. */
    background: #12161A;
    color: #fff;
    font: 600 14px/1 inherit;
    min-height: 50px;
    padding: 0 26px;
    border-radius: 999px;
    cursor: pointer;
    box-shadow: 0 10px 26px rgba(10, 12, 14, .3);
}

.event-fab:hover { background: #2A3138; }

/* Phones carry a bottom tab bar, so the button sits above it instead of on top of it. */
@media (max-width: 720px) {
    .event-fab { bottom: calc(var(--tabbar, 64px) + 14px); }
}

.event-group-head {
    font-size: 11px;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: #5F6469;
    margin-bottom: 14px;
}

/* One meet-up per row, stacked: two per row still cramped the title into three lines and cut the
   description short. The past meet-ups below keep their own compact grid. */
.event-grid {
    display: flex;
    flex-direction: column;
    gap: 12px;
    max-width: 1280px;
    margin: 0 auto 44px;
}

.event-form {
    max-width: 720px;
    margin: 0 auto 34px;
    background: var(--card);
    border: 1px solid rgba(18, 22, 26, .09);
    border-radius: 14px;
    padding: 16px 18px;
}

/* The date block and the body always stay side by side; only the inside of the body wraps. */
.event-card {
    display: flex;
    flex-wrap: nowrap;
    background: var(--card);
    border: 1px solid rgba(18, 22, 26, .09);
    border-radius: 18px;
    overflow: hidden;
}

/* Place suggestions under the "where" field of the meet-up form. */
.place-pick { position: relative; }
.place-list {
    position: absolute;
    left: 0;
    right: 0;
    top: calc(100% + 4px);
    z-index: 5;
    max-height: 244px;
    overflow-y: auto;
    background: #fff;
    border: 1px solid rgba(18, 22, 26, .12);
    border-radius: 12px;
    box-shadow: 0 14px 34px rgba(10, 20, 35, .14);
    padding: 4px;
}
.place-item {
    display: block;
    width: 100%;
    text-align: left;
    background: none;
    border: 0;
    border-radius: 9px;
    padding: 8px 10px;
    font-family: inherit;
    color: inherit;
    cursor: pointer;
}
.place-item:hover { background: rgba(18, 58, 107, .07); }
.place-item b { display: block; font-size: 13.5px; font-weight: 500; line-height: 1.35; }
.place-item span {
    display: block;
    font-size: 11.5px;
    color: var(--muted);
    line-height: 1.35;
    margin-top: 1px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Arrived from a shared /su-kien/12 link: the card rings for a moment so the reader sees WHICH
   meet-up the link meant, then settles back into the list. */
.event-card { transition: box-shadow .25s; }
.event-card.is-spot { box-shadow: 0 0 0 3px rgba(18, 58, 107, .28); }

/* The date block is a dark blue field, readable from across the room. */
.event-when {
    flex: 0 0 clamp(88px, 8vw, 116px);
    background: #123A6B;
    color: #fff;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 5px;
    padding: 24px 12px;
    min-height: 140px;
}

.event-when b { font: 400 clamp(32px, 2.6vw, 44px)/.9 var(--serif); letter-spacing: -.03em; font-weight: 400; }
.event-when .month { font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: #DFE3EA; white-space: nowrap; }
.event-when .time { font-size: 12.5px; color: #D4DCE4; }

/* Text on the left, the cluster of faces and buttons on the right, both centred against each other.
   Below roughly 760px the right cluster wraps under the text and the date block stays where it is. */
.event-body {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 16px clamp(18px, 2vw, 30px);
    padding: clamp(16px, 1.8vw, 24px);
}

.event-main { flex: 1 1 360px; min-width: 0; }
.event-body h3 { font: 400 clamp(19px, 1.6vw, 24px)/1.3 var(--serif); margin: 0 0 8px; }

/* The right cluster: who joined, the two buttons, then the host line. */
/* Everything in the right cluster hangs off the same right edge - the row of faces, the two
   buttons and the host line. The cluster is as wide as its widest row, so aligning left would
   leave the buttons at a different spot in every card and the column would read as ragged. */
.event-side { flex: 0 0 auto; display: flex; flex-direction: column; align-items: flex-end; gap: 12px; text-align: right; }

.event-place {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 14px;
    font-size: 13px;
    color: #5F6469;
    margin-bottom: 12px;
}

/* The place doubles as a map link. Keep it the same size and colour as plain text, with just an
   underline on hover: a bright blue link inside the card would shout louder than the title. */
/* Marks an optional field in a form label, in a lighter tone than the label itself. */
.field-hint { font-weight: 400; text-transform: none; letter-spacing: 0; color: #9A9EA3; }

.event-map { display: inline-flex; align-items: center; gap: 6px; color: inherit; }
.event-map:hover { text-decoration: underline; }
.event-map svg { flex: 0 0 auto; color: var(--accent); }

/* The whole description shows: there is no detail page to carry the rest, so a clamp would simply
   swallow it. Only the reading width stays capped, a line across the full 1280px band is tiring. */
.event-body p {
    margin: 0;
    max-width: 76ch;
    font-size: 13px;
    line-height: 1.65;
    color: #4A4F54;
}

.event-guests { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.event-faces { display: flex; }

/* Faces of those who joined overlap like a group photo line-up. */
.event-guest {
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background: rgba(18, 22, 26, .07);
    border: 2px solid var(--card);
    margin-left: -8px;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    font: 400 13px/1 var(--serif);
    color: rgba(18, 22, 26, .5);
    flex: 0 0 auto;
}

.event-faces .event-guest:first-child { margin-left: 0; }
.event-guest img { width: 100%; height: 100%; object-fit: cover; }
.event-guest.more { background: rgba(18, 58, 107, .1); font: 500 11.5px/1 var(--sans); color: #123A6B; }
.event-count { font-size: 12.5px; color: #5F6469; }

/* The two buttons sit on one line inside the right cluster; never let them wrap. */
.event-acts { display: flex; align-items: center; gap: 10px; flex-wrap: nowrap; }
.event-acts .btn { white-space: nowrap; min-height: 44px; font-size: 13px; }
.event-acts .btn-primary, .event-acts .btn-ghost { padding: 0 18px; }
.event-acts .btn-ghost + .btn-ghost { padding: 0 15px; }
.event-acts .spacer { flex: 1 1 auto; }
/* Host line, with the cancel link beside it when the viewer is allowed to remove the meet-up. */
.event-by { display: flex; align-items: center; gap: 12px; font-size: 12px; color: #5F6469; }
.event-host { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.event-drop {
    flex: 0 0 auto;
    border: 0;
    background: transparent;
    padding: 0;
    font-size: 12px;
    color: #5F6469;
    text-decoration: underline;
    cursor: pointer;
}

.event-empty {
    text-align: center;
    padding: 54px 20px;
    background: var(--card);
    border: 1px solid rgba(18, 22, 26, .09);
    border-radius: 18px;
}

.event-empty h3 { font: 400 21px/1.25 var(--serif); margin: 0 0 10px; }
.event-empty p { margin: 0; font-size: 13.5px; line-height: 1.65; color: #5F6469; }

.event-past-wrap { border-top: 1px solid rgba(18, 22, 26, .10); padding-top: 26px; }

.event-past-grid {
    display: flex;
    flex-direction: column;
    gap: 12px;
    max-width: 1280px;
    margin: 0 auto;
}

/* A past meet-up wears the same card as an upcoming one, with the date block in reverse colours:
   white on the inside, so the card reads as finished without dimming the text. */
.event-past .event-when {
    background: #fff;
    color: #123A6B;
    border-right: 1px solid rgba(18, 58, 107, .16);
}

.event-past .event-when .month { color: #5F6469; }
.event-past .event-when .time { color: var(--muted); }
.event-done { color: #5F6469; }
.event-sep { color: #9A9EA3; }

@media (max-width: 720px) {
    /* On phones the cluster sits under the text, so it reads from the left like everything else. */
    .event-side { align-items: stretch; text-align: left; }
}

@media (max-width: 640px) {
    .event-acts { flex-direction: row; }
    .event-acts .btn { flex: 0 1 auto; }
}


/* --- Directory tab --- */

/* Heading for the directory and events tabs: centred, thin rules on both sides, like the rail headings. */
.panel-head { text-align: center; margin-bottom: 26px; }

.panel-head h1 {
    font: 400 clamp(28px, 5vw, 44px)/1.1 var(--serif);
    margin: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 14px;
}

.panel-head h1::before,
.panel-head h1::after { content: ''; width: clamp(20px, 5vw, 54px); height: 1px; background: rgba(18, 22, 26, .16); }
.panel-head p { margin: 12px auto 0; font-size: 14px; line-height: 1.6; color: #5F6469; }

.directory-wrap { padding: clamp(26px, 4vw, 44px) clamp(16px, 4vw, 40px) 70px; max-width: 1280px; margin: 0 auto; }

/* --- Classroom map on the directory tab -----------------------------------
   The class is drawn the way it was seen from the back of the room: the board on top, the
   teacher desk to one side under it, then nineteen double desks in three columns. Flat 2D on
   purpose, no fake 3D shadows. Every size comes from clamp() so the map needs no media query. */

.map-tools { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; margin-bottom: clamp(14px, 2vw, 22px); }
/* The class manager carries three controls on this row, so on a narrow screen the search box takes
   a line of its own. Everyone else has two controls and still fits on one line. */
@media (max-width: 599px) { .map-tools.has-arrange .map-search { flex: 1 1 100%; order: 2; } }
.map-search { flex: 1 1 150px; min-width: 0; position: relative; display: flex; align-items: center; }
.map-search svg { position: absolute; left: 14px; pointer-events: none; }

.map-search input {
    width: 100%;
    min-height: 46px;
    padding: 0 14px 0 38px;
    border: 1px solid rgba(18, 22, 26, .16);
    border-radius: 999px;
    background: #fff;
    font-size: 14px;
    color: var(--ink);
    outline: none;
}

.map-search input:focus { border-color: #123A6B; }

.map-view-toggle {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    min-height: 46px;
    padding: 0 15px;
    border: 1px solid rgba(18, 22, 26, .18);
    border-radius: 999px;
    background: #fff;
    color: var(--ink);
    font-size: 13px;
    font-family: inherit;
    cursor: pointer;
    white-space: nowrap;
}

.map-view-toggle:hover { border-color: #123A6B; color: #123A6B; }

/* The rearrange button while it is on, so a tap on a seat clearly means moving someone. */
.map-view-toggle.is-on { background: #123A6B; border-color: #123A6B; color: #fff; }
.map-view-toggle.is-on:hover { color: #fff; }

.map-arrange-hint {
    margin: 0 0 clamp(10px, 1.4vw, 16px);
    padding: 10px 14px;
    border: 1px solid rgba(18, 58, 107, .2);
    border-radius: 12px;
    background: rgba(18, 58, 107, .05);
    color: #123A6B;
    font-size: 13px;
    line-height: 1.5;
}

/* Pinching widens the inner frame instead of scaling it, so the outer frame really scrolls and
   the type stays sharp. */
.classroom-scroll { overflow: auto; touch-action: pan-x pan-y pinch-zoom; -webkit-overflow-scrolling: touch; padding-bottom: 4px; }
.classroom-zoom { width: 100%; margin: 0 auto; }
.classroom-map { max-width: 960px; margin: 0 auto; }

.board {
    position: relative;
    overflow: hidden;
    border: clamp(5px, .7vw, 8px) solid #C3AE8C;
    border-radius: 5px;
    background: #2E4038;
    padding: clamp(15px, 2.6vw, 30px) clamp(14px, 2vw, 26px);
    text-align: center;
}

.board-chalk {
    position: absolute;
    inset: 0;
    background: repeating-linear-gradient(114deg, rgba(255, 255, 255, .05) 0 2px, transparent 2px 9px);
    opacity: .7;
    pointer-events: none;
}

/* Ba năm cấp ba viết cạnh nhau trên bảng. Năm đang xem sáng và to, hai năm kia lùi hẳn về sau
   như chữ phấn xoá chưa sạch, nhưng vẫn đọc được và vẫn bấm được. */
.board-names { position: relative; display: flex; align-items: baseline; justify-content: center; gap: clamp(14px, 3vw, 30px); }

.board-name {
    position: relative;
    padding: 0;
    border: 0;
    background: transparent;
    cursor: pointer;
    font: 400 clamp(18px, 3vw, 30px)/1 var(--serif);
    color: #F1EFE4;
    letter-spacing: .06em;
    opacity: .38;
    transition: opacity .18s, font-size .18s;
}

.board-name.is-on { font-size: clamp(30px, 5.4vw, 56px); letter-spacing: .08em; opacity: .9; }
.board-name:focus-visible { outline: 2px solid rgba(241, 239, 228, .7); outline-offset: 4px; border-radius: 4px; }
.board-years { position: relative; font: italic 400 clamp(12px, 1.5vw, 17px)/1 var(--serif); color: #DCD9CB; opacity: .72; margin-top: clamp(8px, 1.2vw, 13px); letter-spacing: .05em; }

/* The teacher desk sat off to one side of the room, not in the middle. */
.teacher-desk-row { display: flex; justify-content: flex-start; margin-top: clamp(12px, 1.8vw, 20px); }
/* The card hugs its text instead of stretching across the row: the line under the name is short,
   so a wide card would leave the right side empty. */
.teacher-desk-box { width: fit-content; max-width: min(100%, 300px); }

.teacher-desk {
    display: flex;
    align-items: center;
    gap: clamp(8px, 1.1vw, 13px);
    width: 100%;
    text-align: left;
    padding: clamp(6px, .8vw, 9px);
    border: 1px solid #D6C4A4;
    border-radius: 7px;
    background: linear-gradient(#F0E4CF, #E5D7BD);
    font-family: inherit;
    cursor: pointer;
    transition: opacity .16s ease;
}

.teacher-desk:hover { border-color: #123A6B; }
.teacher-desk .seat-face { flex: 0 0 auto; width: clamp(38px, 4.4vw, 54px); height: clamp(38px, 4.4vw, 54px); }
.teacher-desk-body { min-width: 0; line-height: 1.3; }
.teacher-desk-name { display: block; font-size: clamp(11.5px, 1.2vw, 14px); font-weight: 500; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.teacher-desk-sub { display: block; font-size: clamp(9.5px, 1vw, 11.5px); color: #5F6469; margin-top: 3px; }

.desk-legs { display: flex; justify-content: space-between; padding: 0 12%; }
.desk-legs i { width: 2px; height: clamp(8px, 1vw, 12px); background: #C9B693; }
.desk-label { text-align: center; font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase; color: #8A8F94; margin-top: 5px; }

.desk-rows { display: flex; flex-direction: column; gap: clamp(11px, 1.7vw, 20px); margin-top: clamp(18px, 2.8vw, 32px); }
/* The gap between the columns is the aisle between the rows of desks. */
.desk-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(10px, 2vw, 28px); }

.seat-desk {
    display: flex;
    gap: clamp(4px, .6vw, 7px);
    padding: clamp(4px, .6vw, 7px);
    border: 1px solid #D6C4A4;
    border-radius: 7px;
    background: linear-gradient(#F0E4CF, #E5D7BD);
}

.seat-desk + .desk-legs { padding: 0 14%; }
.seat-desk + .desk-legs i { height: clamp(7px, .9vw, 11px); }

.seat {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 0;
    border: 0;
    background: none;
    font-family: inherit;
    cursor: pointer;
    transition: transform .16s ease, opacity .16s ease;
}

.seat:hover { transform: translateY(-3px); }

.seat-face {
    position: relative;
    width: 100%;
    aspect-ratio: 1;
    border-radius: 9px;
    background: #EAE6DE;
    border: 1px solid rgba(18, 22, 26, .12);
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
}

.seat-face img { width: 100%; height: 100%; object-fit: cover; display: block; }
.seat-face-empty { width: 64%; height: 64%; display: block; }
/* The name rides on the lower edge of the picture, centred, over a fade dark enough to stay
   readable on a bright photo as well as on the grey silhouette of a seat with no portrait. */
.seat-name {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 7px 1px 1px;
    text-align: center;
    /* A seat is barely wider than the name it carries on a phone, so the type goes as small as it
       can while a word like Phuong still fits on one line instead of snapping in half. Kept small
       and pushed right down to the edge as well: in a good few portraits the face sits low in the
       frame, and a taller band would land on the chin. */
    font-size: clamp(6.9px, 1.55vw, 10.5px);
    line-height: 1.1;
    letter-spacing: -.015em;
    color: #fff;
    text-shadow: 0 1px 2px rgba(10, 14, 20, .8);
    background: linear-gradient(rgba(12, 16, 22, 0), rgba(12, 16, 22, .78));
}

/* A phone shows the shortened name, a wide screen shows the whole one. */
.seat-name-full { display: none; }

@media (min-width: 700px) {
    .seat-name-full { display: inline; }
    .seat-name-short { display: none; }

    /* Room for a name that runs to two lines, and the band sits a touch higher off the edge. */
    .seat-name { padding: 8px 4px 6px; }
}

/* Somebody the search left out fades back, the one it found keeps a ring. */
.seat.is-dim, .teacher-desk.is-dim { opacity: .28; }
.seat.is-hit .seat-face, .teacher-desk.is-hit { box-shadow: 0 0 0 2px #123A6B; }

/* The seat picked up for a move: a heavier ring and a small lift, clearly different from the
   highlight the name search draws. */
.seat.is-picked .seat-face { box-shadow: 0 0 0 3px #123A6B, 0 8px 18px rgba(18, 58, 107, .28); transform: translateY(-2px); }
.seat.is-picked .seat-name { background: linear-gradient(rgba(18, 58, 107, 0), rgba(18, 58, 107, .88)); }

.map-foot { display: flex; align-items: center; justify-content: center; gap: 12px; flex-wrap: wrap; margin-top: clamp(16px, 2.2vw, 24px); font-size: 12px; color: #8A8F94; }

.map-foot-btn {
    border: 1px solid rgba(18, 22, 26, .18);
    background: #fff;
    color: #123A6B;
    font-size: 12.5px;
    font-family: inherit;
    min-height: 38px;
    padding: 0 14px;
    border-radius: 999px;
    cursor: pointer;
}

.map-note { text-align: center; font-size: 11.5px; color: #8A8F94; margin-top: clamp(22px, 3vw, 34px); }

@media (max-width: 699px) {
    .teacher-desk-box { max-width: min(100%, 260px); }
}

/* --- The sheet for one person --------------------------------------------- */

.person-sheet {
    position: relative;
    width: 100%;
    max-width: 388px;
    background: #fff;
    border-radius: 20px;
    box-shadow: 0 24px 60px rgba(10, 20, 35, .24);
    padding: clamp(20px, 3vw, 28px);
}

.person-close {
    position: absolute;
    top: 12px;
    right: 12px;
    width: 38px;
    height: 38px;
    border: 0;
    border-radius: 50%;
    background: rgba(18, 22, 26, .06);
    color: var(--ink);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}

.person-close:hover { background: rgba(18, 22, 26, .12); }
.person-head { display: flex; flex-direction: column; align-items: center; text-align: center; }

.person-face {
    width: clamp(96px, 26vw, 124px);
    height: clamp(96px, 26vw, 124px);
    border-radius: 16px;
    background: #EAE6DE;
    border: 1px solid rgba(18, 22, 26, .12);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}

.person-face img { width: 100%; height: 100%; object-fit: cover; display: block; }
.person-face svg { width: 60%; height: 60%; display: block; }
.person-noface { font-size: 11.5px; color: #8A8F94; margin: 9px 0 0; }
.person-sheet h2 { font: 400 clamp(22px, 3vw, 27px)/1.2 var(--serif); margin: 14px 0 0; }
.person-meta { font-size: 12.5px; color: #5F6469; margin-top: 7px; }

.person-count {
    display: flex;
    align-items: baseline;
    justify-content: center;
    gap: 7px;
    margin-top: 16px;
    padding: 13px 0;
    border-top: 1px solid rgba(18, 22, 26, .1);
    border-bottom: 1px solid rgba(18, 22, 26, .1);
}

.person-count b { font: 400 24px/1 var(--serif); color: #123A6B; }
.person-count span { font-size: 12.5px; color: #5F6469; }

.person-album {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    background: #123A6B;
    color: #fff;
    font-size: 14px;
    font-weight: 500;
    min-height: 48px;
    border-radius: 999px;
    margin-top: 16px;
}

.person-album:hover { background: #0C2747; color: #fff; }
.person-contact { display: flex; flex-direction: column; gap: 9px; margin-top: 14px; }
.contact-box { display: flex; align-items: center; gap: 11px; padding: 11px 13px; border: 1px solid rgba(18, 22, 26, .12); border-radius: 14px; }
.contact-box svg { flex: 0 0 auto; }
.contact-body { flex: 1 1 auto; min-width: 0; }
.contact-kind { font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase; color: #8A8F94; }
.contact-value { font-size: 14px; color: var(--ink); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.contact-act {
    flex: 0 0 auto;
    min-height: 44px;
    padding: 0 15px;
    border-radius: 999px;
    border: 1px solid rgba(18, 22, 26, .18);
    color: #123A6B;
    font-size: 13px;
    font-weight: 500;
    display: inline-flex;
    align-items: center;
}

.contact-act.filled { background: #123A6B; color: #fff; border-color: #123A6B; }
.person-locked { margin-top: 15px; padding: 15px; border: 1px solid rgba(18, 58, 107, .18); border-radius: 14px; background: rgba(18, 58, 107, .05); }
.person-locked p { font-size: 13.5px; line-height: 1.6; color: var(--ink); margin: 0; }

.person-locked .btn-outline {
    width: 100%;
    border: 1px solid #123A6B;
    background: #fff;
    color: #123A6B;
    font-size: 13.5px;
    font-weight: 500;
    font-family: inherit;
    min-height: 46px;
    border-radius: 999px;
    cursor: pointer;
    margin-top: 12px;
}

.person-locked .btn-outline:hover { background: #123A6B; color: #fff; }


/* Sign-in prompt for members who are not approved yet, above the list. */
.btn-outline {
    border: 1px solid rgba(18, 58, 107, .4);
    background: transparent;
    color: #123A6B;
    font-size: 13.5px;
    font-weight: 500;
    min-height: 44px;
    padding: 0 20px;
    border-radius: 999px;
    white-space: nowrap;
}


/* The plain list next to the classroom map: one line per classmate, narrow enough to read. */
.directory-list { max-width: 640px; margin: 0 auto; }

.directory-line {
    display: flex;
    align-items: center;
    gap: 13px;
    width: 100%;
    text-align: left;
    padding: 10px 2px;
    border: 0;
    border-bottom: 1px dotted rgba(18, 22, 26, .2);
    background: none;
    font-family: inherit;
    cursor: pointer;
    transition: opacity .16s ease;
}

.directory-line:hover { background: rgba(18, 58, 107, .04); }
.directory-line.is-dim { opacity: .35; }
.directory-line .no { flex: 0 0 auto; width: 24px; text-align: right; font: 400 12.5px/1.4 var(--serif); color: #123A6B; }

.directory-line .face {
    flex: 0 0 auto;
    width: 36px;
    height: 36px;
    border-radius: 9px;
    background: #EAE6DE;
    border: 1px solid rgba(18, 22, 26, .12);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}

.directory-line .face img { width: 100%; height: 100%; object-fit: cover; display: block; }
.directory-line .face svg { width: 62%; height: 62%; }
.directory-line .name { flex: 1 1 auto; min-width: 0; font-size: 14.5px; line-height: 1.4; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.directory-line .tail { flex: 0 0 auto; font-size: 11.5px; color: #5F6469; }

.directory-row {
    background: var(--card);
    border: 1px solid rgba(18, 22, 26, .09);
    border-radius: 14px;
    padding: 13px 15px;
}

.directory-row .who { display: flex; align-items: center; gap: 13px; }
.directory-row .who.link { cursor: pointer; }

.directory-row .face {
    width: 48px;
    height: 48px;
    border-radius: 11px;
    overflow: hidden;
    background: rgba(18, 22, 26, .07);
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    font: 400 20px/1 var(--serif);
    color: rgba(18, 22, 26, .44);
}

.directory-row .face img { width: 100%; height: 100%; object-fit: cover; }
.directory-row .face.pale { opacity: .5; }
.directory-row .body { flex: 1 1 auto; min-width: 0; }
.directory-row .body b { display: block; font-size: 15px; font-weight: 500; line-height: 1.35; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.directory-row .body span { display: block; font-size: 12px; color: #5F6469; margin-top: 3px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The contact row sits under a thin rule, with tabular figures so every digit reads clearly. */
.directory-contact {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 12px;
    padding-top: 11px;
    border-top: 1px solid rgba(18, 22, 26, .07);
}

.directory-contact .lines { flex: 1 1 auto; min-width: 0; }

.directory-contact a,
.directory-contact button {
    flex: 0 0 auto;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: 1px solid rgba(18, 22, 26, .16);
    background: transparent;
    display: flex;
    align-items: center;
    justify-content: center;
    /* One single look for the three channels, signed in or not. The card must not change shape
       once somebody signs in, so the state lives in what a tap does, never in the colours. */
    color: #5F6469;
    border-color: rgba(18, 22, 26, .12);
}

.directory-contact button { cursor: pointer; }
.directory-contact .hint { font-size: 12.5px; color: #5F6469; }



/* Sheet holding the entries that do not fit in the bottom bar on phones. */
/* Panel sliding up from the bottom: a scrim over the page, the panel pinned to the bottom edge. */
.tabbar-sheet {
    position: fixed;
    inset: 0;
    z-index: 60;
    background: rgba(10, 11, 12, .5);
    display: none;
    align-items: flex-end;
    justify-content: center;
}

@media (max-width: 720px) {
    .tabbar-sheet:not([hidden]) { display: flex; }
}

.tabbar-sheet .sheet-card {
    width: 100%;
    max-width: 520px;
    background: #F7F6F3;
    border-radius: 22px 22px 0 0;
    padding: 10px 16px calc(16px + env(safe-area-inset-bottom));
    box-shadow: 0 -18px 50px rgba(4, 12, 26, .22);
    animation: fade-up .2s ease-out;
    max-height: 88vh;
    overflow-y: auto;
    /* Releasing mid-drag snaps the sheet back, see bindSheetDrag() in photos.js. */
    transition: transform .18s ease-out;
    touch-action: pan-y;
}

.sheet-grip { width: 42px; height: 4px; border-radius: 999px; background: rgba(18, 22, 26, .16); margin: 0 auto 14px; }

.sheet-brand { display: flex; align-items: center; gap: 11px; padding: 0 4px 14px; }
.sheet-brand img { width: 34px; height: 34px; border-radius: 50%; flex: 0 0 auto; }
.sheet-brand b { display: block; font: 500 14px/1.2 var(--serif); }
.sheet-brand span { display: block; font-size: 11.5px; color: #5F6469; margin-top: 3px; }

/* Each entry is a 64px card, an easier target than a bare line of text. */
.tabbar-sheet .sheet-item {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    min-height: 64px;
    padding: 0 14px;
    margin-bottom: 8px;
    border: 1px solid rgba(18, 22, 26, .09);
    border-radius: 14px;
    background: var(--card);
    color: var(--ink);
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}

.sheet-icon {
    flex: 0 0 auto;
    width: 42px;
    height: 42px;
    border-radius: 12px;
    background: rgba(18, 58, 107, .07);
    color: #123A6B;
    display: flex;
    align-items: center;
    justify-content: center;
}

.sheet-icon svg { width: 20px; height: 20px; stroke-width: 1.5; }
.sheet-text { flex: 1 1 auto; min-width: 0; }
.sheet-text b { display: block; font-size: 15px; font-weight: 500; }
.sheet-text span { display: block; font-size: 12px; color: #5F6469; margin-top: 2px; }

/* Red badge for the class manager: work is waiting. Red, not the house blue, because this is the
   one entry that asks for something to be done. */
.sheet-dot {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 22px;
    height: 22px;
    margin-right: 6px;
    padding: 0 7px;
    border-radius: 999px;
    background: #B3261E;
    color: #fff;
    font-size: 11.5px;
    font-style: normal;
    font-weight: 600;
}

/* The same warning on the "more" button of the bottom bar, so the sheet does not have to be opened. */
.tabbar .tab-alert {
    position: absolute;
    top: 8px;
    right: calc(50% - 15px);
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: #B3261E;
}

.sheet-go {
    flex: 0 0 auto;
    width: 8px;
    height: 8px;
    border-right: 1.6px solid rgba(18, 22, 26, .3);
    border-top: 1.6px solid rgba(18, 22, 26, .3);
    transform: rotate(45deg);
}

/* Current entry: blue border, filled icon box, a check mark on the right instead of a chevron. */
/* Signing out is the quietest row in the sheet, so a stray thumb does not land on it. */

.tabbar-sheet .sheet-item[aria-current="page"] { background: rgba(18, 58, 107, .06); border-color: rgba(18, 58, 107, .24); }
.tabbar-sheet .sheet-item[aria-current="page"] .sheet-icon { background: #123A6B; color: #fff; }

.tabbar-sheet .sheet-item[aria-current="page"] .sheet-go {
    width: 20px;
    height: 20px;
    border: 0;
    border-radius: 50%;
    background: #123A6B;
    transform: none;
    position: relative;
}

.tabbar-sheet .sheet-item[aria-current="page"] .sheet-go::after {
    content: '';
    position: absolute;
    left: 6.5px;
    top: 5px;
    width: 5px;
    height: 9px;
    border-right: 1.8px solid #fff;
    border-bottom: 1.8px solid #fff;
    transform: rotate(42deg);
}

/* --- Sign-in prompt --- */

.login-google {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    border: 1px solid rgba(18, 22, 26, .14);
    background: #fff;
    color: var(--ink);
    font-size: 15px;
    font-weight: 500;
    padding: 15px 20px;
    border-radius: 12px;
    cursor: pointer;
    margin-bottom: 14px;
    text-decoration: none;
}

.login-google:hover { background: rgba(18, 22, 26, .04); }

.login-note { font-size: 11.5px; line-height: 1.6; color: #5F6469; margin-bottom: 20px; }
.login-off { color: var(--muted); font-size: 13px; }

.login-later {
    border: 0;
    background: transparent;
    color: var(--muted);
    font-size: 13px;
    cursor: pointer;
    padding: 6px 10px;
    min-height: 40px;
}

/* --- Home page ------------------------------------------ */

/* Same width cap and gutter as the card below, so the two blocks stack into one column. */
.home-hero-wrap { max-width: 1280px; margin: 0 auto; padding: clamp(12px, 1.6vw, 20px) clamp(16px, 4vw, 40px) 0; }

.home-hero {
    position: relative;
    /* Same corner as .home-gate right below it. */
    border-radius: 18px;
    min-height: clamp(380px, 44vw, 540px);
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    background: #AEB6BD;
    overflow: hidden;
}

.home-hero img.bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center 35%; }
.home-hero-shade {
    position: absolute;
    inset: 0;
    /* A soft pool of shade behind the words, then a light wash over the whole picture: the faces
       underneath stay visible while the white type keeps something to sit on. */
    background:
        radial-gradient(ellipse 62% 58% at 50% 48%, rgba(6, 16, 32, .46), rgba(6, 16, 32, 0) 70%),
        linear-gradient(to bottom, rgba(8, 20, 40, .26), rgba(8, 20, 40, .56));
}
.home-hero-inner { position: relative; padding: clamp(44px, 6vw, 76px) clamp(16px, 4vw, 40px); max-width: 1280px; margin: 0 auto; color: #fff; }

/* Same as the reunion banner: badge removed, its space kept so the block stays as tall as before,
   and split evenly so the text sits in the middle of the picture rather than below it. */
.home-hero-inner {
    padding-top: calc(clamp(44px, 6vw, 76px) + (clamp(70px, 7.6vw, 88px) + 20px) / 2);
    padding-bottom: calc(clamp(44px, 6vw, 76px) + (clamp(70px, 7.6vw, 88px) + 20px) / 2);
}

.home-hero .eyebrow { font-size: 11px; letter-spacing: .2em; text-transform: uppercase; color: rgba(255, 255, 255, .86); margin-bottom: 14px; }
.home-hero h1 { font: 400 clamp(38px, 7vw, 72px)/1.1 var(--serif); margin: 0 0 14px; letter-spacing: -.02em; color: #fff; }
.home-hero-line { display: flex; align-items: center; justify-content: center; gap: 12px; margin-bottom: 20px; }
.home-hero-line i { width: clamp(24px, 6vw, 64px); height: 1px; background: rgba(255, 255, 255, .48); }
.home-hero-line span { font: italic 400 15px/1 var(--serif); color: rgba(255, 255, 255, .95); letter-spacing: .04em; }
.home-hero p { margin: 0 auto 28px; font-size: 15px; line-height: 1.7; color: rgba(255, 255, 255, .86); }
.home-hero-acts { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 10px; }

.home-btn-light { background: #fff; color: #10305B; font-weight: 600; min-height: 48px; padding: 0 24px; border-radius: 999px; border: 0; }
.home-btn-outline { border: 1px solid rgba(255, 255, 255, .4); background: transparent; color: #fff; min-height: 48px; padding: 0 22px; border-radius: 999px; }

/* Width cap on the padded box, same as the top bar, so the card lines up with the logo. */
.home-gate-wrap { padding: clamp(30px, 4vw, 52px) clamp(16px, 4vw, 40px) 0; max-width: 1280px; margin: 0 auto; }

.home-gate {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    background: var(--card);
    border: 1px solid rgba(18, 22, 26, .09);
    border-radius: 18px;
    overflow: hidden;
    max-width: 1280px;
    margin: 0 auto;
    min-height: clamp(300px, 22vw, 400px);
}

.home-mosaic {
    flex: 1 1 340px;
    min-width: 0;
    align-self: stretch;
    min-height: 220px;
    display: grid;
    grid-template-columns: 1.7fr 1fr 1fr;
    grid-template-rows: 1fr 1fr;
    gap: 4px;
    background: rgba(18, 22, 26, .06);
}

.home-mosaic button { border: 0; padding: 0; background: rgba(18, 22, 26, .06); cursor: pointer; overflow: hidden; }
.home-mosaic button:first-child { grid-column: 1; grid-row: 1 / span 2; }
.home-mosaic img { width: 100%; height: 100%; object-fit: cover; display: block; }

.home-gate-text {
    flex: 0 0 clamp(300px, 34%, 520px);
    min-width: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: clamp(24px, 2.6vw, 40px);
}

.home-gate-text .eyebrow { font-size: 11px; letter-spacing: .18em; text-transform: uppercase; color: #5F6469; margin-bottom: 12px; }
.home-gate-text h2 { font: 400 clamp(26px, 2.8vw, 38px)/1.12 var(--serif); margin: 0 0 14px; }
.home-gate-text p {
    margin: 0 0 20px;
    font-size: 14.5px;
    line-height: 1.7;
    color: #4A4F54;
}
.home-stats { display: flex; flex-wrap: nowrap; gap: clamp(14px, 3.4vw, 22px); margin-bottom: 24px; }
.home-stats > div { min-width: 0; }
.home-stats b { display: block; font: 400 26px/1 var(--serif); color: #123A6B; font-weight: 400; }
/* Số chưa về thì ô vẫn phải chiếm đúng chừng ấy chỗ, không thì lúc số về cả khối cao lên và
   đẩy hai nút bên dưới tụt xuống. */
.home-stats b:empty::before { content: "\00a0"; }
.home-stats span { display: block; font-size: 11.5px; color: #5F6469; margin-top: 6px; }
.home-gate-acts { display: flex; gap: 10px; flex-wrap: wrap; }

/* Same band as the top bar: the width cap goes on the padded box itself, so the text inside
   starts under the logo. Capping an inner child instead pushed the content 40px wider each side. */
.home-block {
    padding: clamp(34px, 4.4vw, 58px) clamp(16px, 4vw, 40px) 0;
    max-width: 1280px;
    margin: 0 auto;
}
/* Khối cuối trang chủ: đuôi 70px cũ để lại một dải trống dài trước khối Khoảnh khắc chung
   ngay bên dưới, mà khối ấy đã có nền và đệm riêng rồi. */
.home-block.last { padding-bottom: clamp(22px, 2.6vw, 32px); }
.panel-head.small h2 { font: 400 24px/1.15 var(--serif); margin: 0; display: flex; align-items: center; justify-content: center; gap: 14px; }
.panel-head.small h2::before,
.panel-head.small h2::after { content: ''; width: clamp(20px, 5vw, 54px); height: 1px; background: rgba(18, 22, 26, .16); }
.panel-head.small p { margin: 9px auto 0; font-size: 13.5px; color: #5F6469; }
.panel-head.small { text-align: center; margin-bottom: 20px; }
.home-more { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; margin-top: 18px; }

/* Shown on computers only. The bottom bar on phones already carries the same link. */
@media (max-width: 879px) { .only-desk { display: none; } }

/* Meet-ups on the home page: compact cards, button row on one line with no wrapping. */
.home-events {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 12px;
    max-width: 1280px;
    margin: 0 auto;
    align-items: stretch;
}

@media (max-width: 1080px) {
    .home-events { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 700px) {
    .home-events { grid-template-columns: minmax(0, 1fr); }
}

.home-event { display: flex; align-items: stretch; gap: 16px; background: var(--card); border: 1px solid rgba(18, 22, 26, .09); border-radius: 16px; padding: 16px; }

.home-event .day {
    flex: 0 0 auto;
    width: 64px;
    align-self: stretch;
    padding: 14px 0;
    border-radius: 12px;
    background: #123A6B;
    color: #fff;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 5px;
}

.home-event .day b { font: 400 24px/1 var(--serif); font-weight: 400; }
.home-event .day .month { font-size: 10px; letter-spacing: .12em; text-transform: uppercase; color: #DFE3EA; }
.home-event .day .time { font-size: 11px; color: #CBD4DE; }
.home-event .body { flex: 1 1 auto; min-width: 0; }
.home-event .title { font-size: 15.5px; font-weight: 500; line-height: 1.35; margin-bottom: 6px; }
.home-event .when { font-size: 12.5px; color: #5F6469; line-height: 1.5; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.home-event .joined { font-size: 12.5px; color: #5F6469; margin-top: 4px; }
.home-event .acts { display: flex; align-items: center; gap: 8px; margin-top: 14px; min-width: 0; }
.home-event .acts .btn { min-height: 36px; padding: 0 15px; font-size: 12.5px; white-space: nowrap; }
.home-event .acts .spacer { flex: 1 1 auto; min-width: 6px; }
.home-event .host { flex: 0 1 auto; min-width: 0; font-size: 11.5px; color: #5F6469; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* An empty state is just a gentle note, not a white card the size of a real content block:
   in a month with no birthdays that white card would be the loudest thing on the home page. */
/* A thin framed note, sized to its own text so it stays on one line on desktop and only wraps
   when the screen is narrower than the sentence. */
.home-empty {
    width: max-content;
    max-width: 100%;
    margin: 0 auto;
    padding: 12px 22px;
    border: 1px solid rgba(18, 22, 26, .12);
    border-radius: 999px;
    text-align: center;
    font-size: 13px;
    line-height: 1.7;
    color: var(--muted);
}

.home-birthdays { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 250px), 1fr)); gap: 10px; max-width: 1280px; margin: 0 auto; }
.home-birth { display: flex; align-items: center; gap: 13px; background: var(--card); border: 1px solid rgba(18, 22, 26, .09); border-radius: 14px; padding: 12px 14px; }

.home-birth .face {
    flex: 0 0 auto;
    width: 48px;
    height: 48px;
    border-radius: 11px;
    background: rgba(18, 22, 26, .07);
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    font: 400 20px/1 var(--serif);
    color: rgba(18, 22, 26, .44);
}

.home-birth .face img { width: 100%; height: 100%; object-fit: cover; }
.home-birth .body { flex: 1 1 auto; min-width: 0; }
.home-birth b { display: block; font-size: 14.5px; font-weight: 500; line-height: 1.35; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.home-birth span { display: block; font-size: 12px; color: #5F6469; margin-top: 3px; white-space: nowrap; }

/* Only the Zalo button is round: the friend name inside the card is a link too. */
.home-birth .zalo-link {
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    border: 1px solid rgba(18, 22, 26, .16);
    display: flex;
    align-items: center;
    justify-content: center;
    color: #123A6B;
}

/* Three teachers, three fixed columns: with auto-fill a 2400px screen pushes them all to the left. */
.home-teachers { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(12px, 1.6vw, 20px); max-width: 1280px; margin: 0 auto; }
/* Register style teacher card: a large year number, and only a small square photo because it is
   cropped from an album rather than a portrait. */
.home-teacher {
    background: var(--card);
    border: 1px solid rgba(18, 22, 26, .09);
    border-radius: 16px;
    padding: clamp(18px, 1.9vw, 24px);
    display: flex;
    flex-direction: column;
    cursor: pointer;
    text-align: left;
}

.tc-head { display: flex; align-items: center; gap: 14px; padding-bottom: 15px; border-bottom: 1px solid rgba(18, 22, 26, .10); }
.tc-grade { font: 400 clamp(38px, 3.4vw, 50px)/.82 var(--serif); color: #123A6B; letter-spacing: -.04em; }
.tc-role { padding-left: 14px; border-left: 1px solid rgba(18, 22, 26, .14); }
.tc-role b { display: block; font-size: 10.5px; font-weight: 600; letter-spacing: .18em; text-transform: uppercase; color: #123A6B; }
.tc-role span { display: block; font-size: 12px; color: #5F6469; margin-top: 4px; }

.tc-name { display: flex; align-items: center; gap: 14px; padding-top: 16px; margin: 0 0 14px; }
.tc-face { flex: 0 0 auto; width: 64px; height: 64px; border-radius: 12px; overflow: hidden; background: rgba(18, 22, 26, .07); }
.tc-face img { width: 100%; height: 100%; object-fit: cover; }
.tc-who b { display: block; font-size: 10.5px; font-weight: 600; letter-spacing: .18em; text-transform: uppercase; color: #5F6469; }
.tc-who em { display: block; font: italic 400 clamp(20px, 1.8vw, 25px)/1.2 var(--serif); color: var(--ink); margin-top: 4px; }

/* Same here: the teacher's story has nowhere else to be read in full. */
.tc-story {
    margin: 0 0 16px;
    font-size: 13.5px;
    line-height: 1.65;
    color: #4A4F54;
}

.tc-foot { margin-top: auto; display: flex; align-items: center; justify-content: space-between; gap: 12px; font-size: 12.5px; }
.tc-foot span:first-child { color: #5F6469; white-space: nowrap; }
.tc-open { color: #123A6B; white-space: nowrap; }

@media (max-width: 720px) {
    .home-teachers { grid-template-columns: minmax(0, 1fr); }
}

.home-old-scroll { overflow-x: auto; scrollbar-width: none; }
.home-old-scroll::-webkit-scrollbar { display: none; }
.home-old { display: flex; gap: 10px; width: max-content; max-width: 100%; margin: 0 auto; padding: 0 clamp(16px, 4vw, 40px) 6px; }
.home-old-cell { flex: 0 0 auto; width: 200px; }
.home-old-cell button { width: 200px; height: 200px; border-radius: 10px; border: 0; padding: 0; background: rgba(18, 22, 26, .07); cursor: pointer; overflow: hidden; }
.home-old-cell img { width: 100%; height: 100%; object-fit: cover; display: block; }
.home-old-cell .sender { font-size: 12px; color: #5F6469; margin-top: 8px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.home-book {
    max-width: 1280px;
    margin: 0 auto;
    background: var(--card);
    border: 1px solid rgba(18, 22, 26, .09);
    border-radius: 18px;
    padding: clamp(20px, 2.4vw, 34px) clamp(18px, 2.4vw, 36px);
}

.home-book-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    flex-wrap: wrap;
    padding-bottom: 14px;
    margin-bottom: 6px;
    border-bottom: 1px solid rgba(18, 22, 26, .10);
    font-size: 11px;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: #5F6469;
}

.home-book-list { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0 clamp(20px, 3vw, 52px); }
.home-book-row { display: flex; align-items: baseline; gap: 12px; padding: 9px 0; border-bottom: 1px dotted rgba(18, 22, 26, .2); background: none; border-left: 0; border-right: 0; border-top: 0; width: 100%; text-align: left; }
.home-book-row .no { flex: 0 0 auto; width: 22px; text-align: right; font: 400 12.5px/1.4 var(--serif); color: #123A6B; }
.home-book-row .name { flex: 1 1 auto; min-width: 0; font-size: 14px; line-height: 1.4; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.home-book-row .tail { flex: 0 0 auto; font-size: 11.5px; color: #5F6469; letter-spacing: .02em; }
.home-book-row { text-decoration: none; color: inherit; }
.home-book-row.link { cursor: pointer; }
.home-book-row.link:hover .name { color: #123A6B; }
/* Names without photos used to be greyed out, which next to black names looked like two different
   type styles. The right column already says there are no photos, so all names share one colour. */

@media (max-width: 1080px) {
    .home-book-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 700px) {
    .home-book-list { grid-template-columns: minmax(0, 1fr); }
}

/* --- Reunion sub-tabs --- */

.subtabs {
    position: sticky;
    z-index: 32;
    background: rgba(242, 240, 236, .94);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border-bottom: 1px solid rgba(18, 22, 26, .10);
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior: contain;
    scrollbar-width: none;
}

.subtabs::-webkit-scrollbar { display: none; }
/* Sub-tabs must look clearly different from the main menu: the main menu is rounded pills, the
   sub-tabs are plain underlined text. Matching styles would read as two identical menus. */
.subtabs-inner {
    display: flex;
    align-items: center;
    gap: clamp(14px, 2vw, 26px);
    width: max-content;
    /* No max-width here. Capping the row at the screen width kept the trailing spacer out of the
       scroll width, so the last tab stayed cut off even at the end of the scroll. The row is
       max-content anyway, so it only grows past the screen when it has to scroll. */
    margin: 0 auto;
    padding: 0 clamp(16px, 4vw, 40px);
}

.subtabs-date {
    flex: 0 0 auto;
    font: italic 400 14px/1 var(--serif);
    color: #5F6469;
    padding-right: clamp(14px, 2vw, 26px);
    border-right: 1px solid rgba(18, 22, 26, .14);
    white-space: nowrap;
}

.subtabs button {
    flex: 0 0 auto;
    border: 0;
    border-bottom: 2px solid transparent;
    background: transparent;
    color: #5F6469;
    font-size: 14px;
    font-weight: 500;
    letter-spacing: 0;
    min-height: 44px;
    padding: 2px 2px 0;
    border-radius: 0;
    cursor: pointer;
    white-space: nowrap;
}

.subtabs button[aria-pressed="true"] { border-bottom-color: #123A6B; color: #123A6B; font-weight: 600; }


/* --- Scenes and keepsakes sub-tab --- */

/* The outer frame is full bleed so the grid spans everything; the text stays centred via .panel-head. */
.scenes-wrap { padding: clamp(26px, 5vw, 48px) clamp(16px, 4vw, 40px) 70px; }
.scene-grid { column-count: 5; column-gap: 10px; }
.scene-grid button { display: block; width: 100%; border: 0; padding: 0; background: rgba(18, 22, 26, .06); border-radius: 10px; overflow: hidden; cursor: pointer; break-inside: avoid; margin-bottom: 10px; }
.scene-grid button:not([style*="aspect-ratio"]) { aspect-ratio: 4 / 3; }
.scene-grid img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* The empty state spans the whole row instead of shrinking into a single cell on the left. */
.home-events .home-empty,
.home-birthdays .home-empty { grid-column: 1/-1; }

/* --- Old days tab --- */

.old-wrap { padding: clamp(26px, 4vw, 44px) clamp(16px, 4vw, 40px) 70px; max-width: 1280px; margin: 0 auto; }

.old-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: 20px;
    padding-bottom: 18px;
    margin-bottom: clamp(26px, 3vw, 40px);
    border-bottom: 1px solid rgba(18, 22, 26, .14);
}

.old-head-text { flex: 1 1 320px; min-width: 0; }
.old-head-text .eyebrow { font-size: 11px; letter-spacing: .18em; text-transform: uppercase; color: #5F6469; margin-bottom: 10px; }
.old-head-text h1 { font: 400 clamp(30px, 5vw, 50px)/1.1 var(--serif); margin: 0 0 12px; }
.old-head-text p { margin: 0; font-size: 14.5px; line-height: 1.65; color: #4A4F54; }
.old-head-side { flex: 0 0 auto; display: flex; align-items: center; gap: 18px; }
.old-count b { display: block; font: 400 30px/1 var(--serif); color: #123A6B; }
.old-count span { display: block; font-size: 12px; color: #5F6469; margin-top: 5px; }

/* Six per row, fixed columns rather than auto-fill. */
.old-grid { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: clamp(14px, 1.6vw, 26px); }

@media (max-width: 1120px) { .old-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 800px) { .old-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 560px) { .old-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* Each shot is a print pasted in an album: white border, slight tilt, drop shadow. */
/* The paper is the cell itself, not the button: the contributor name below carries a link of its
   own, so it cannot live inside the button. */
.old-cell {
    padding: 9px 9px 0;
    background: #fff;
    border-radius: 3px;
    box-shadow: 0 3px 14px rgba(10, 12, 14, .11);
    transition: transform .18s, box-shadow .18s;
}

.old-cell button {
    display: block;
    width: 100%;
    border: 0;
    padding: 0;
    background: none;
    cursor: pointer;
}

.old-cell.tilt-0 { transform: rotate(-0.9deg); }
.old-cell.tilt-1 { transform: rotate(0.7deg); }
.old-cell.tilt-2 { transform: rotate(-0.3deg); }
.old-cell:hover { transform: rotate(0deg); box-shadow: 0 10px 26px rgba(10, 12, 14, .18); }

.old-cell .paper { position: relative; display: block; aspect-ratio: 1; overflow: hidden; }
.old-cell .paper img { width: 100%; height: 100%; object-fit: cover; display: block; }
.old-cell .paper i { position: absolute; inset: 0; background: linear-gradient(135deg, rgba(190, 143, 42, .09), transparent 60%); }

.old-cell .giver { display: block; text-align: center; padding: 9px 4px 11px; }
.old-cell .giver b { display: block; font-size: 9.5px; font-weight: 600; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); }
.old-cell .giver span { display: block; font: 400 12.5px/1.4 var(--serif); color: #4A4F54; margin-top: 3px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }


/* --- Remaining full width blocks, brought back into the band --- */
/* One 1280px content band across the site; backgrounds stay full bleed so no edge shows. */

.panel-head,
.home-more,
.strip-head,
.grid-more,
.page-head,
.roll-head,
.roll-grid,
.roll-teachers,
.roll-guests,
.teacher-grid,
.teacher-turn,
.mine-head,
.profile-cover { max-width: 1280px; margin-left: auto; margin-right: auto; }

/* The most-liked rail spans the whole viewport like the photo grid; its heading stays centred. */


/* --- Phone fixes --- */

@media (max-width: 760px) {
    /* Album gateway: the text takes the full card width, no more narrow 300px column. */
    .home-gate-text { flex: 1 1 100%; }
}

@media (max-width: 560px) {
    /* The three gateway figures must fit on one row on a phone. */
    .home-stats b { font-size: 23px; }
    .home-stats span { font-size: 11px; }

    /* Reunion sub-tabs. Squeezing every tab onto one screen put them 4px apart and the row read
       as one long word, so the row keeps its natural width, breathes, and scrolls sideways. */
    .subtabs-date { display: none; }
    .subtabs-inner {
        width: max-content;
        gap: 20px;
        justify-content: flex-start;
        padding: 0 0 0 16px;
    }
    .subtabs-inner::after { flex-basis: 16px; }
    .subtabs button { font-size: 13.5px; }

    /* Viewer: like, who is in it, download and share all share one row. */
    .viewer-bottom { padding: 12px 10px calc(14px + env(safe-area-inset-bottom)); }
    .viewer-actions { gap: 6px; }
    .viewer-actions .spacer { display: none; }
    .viewer-actions .btn {
        min-height: 40px;
        padding: 0 10px;
        font-size: 12.5px;
        gap: 5px;
        min-width: 0;
        white-space: nowrap;
    }
}

/* Tablet width: the tab cluster is absolutely centred, so it must leave room for the logo on the
   left and the buttons on the right, otherwise the tabs cover the sign-in button. */
/* Tablet width: the tab cluster leaves absolute centring and flows in the row. A max-width here
   gives six tabs only 440px of the 550px they need, clipping the first and last tab with no hint
   that the row scrolls, because the cluster sits outside the flow. */
@media (max-width: 1080px) and (min-width: 720px) {
    .tabs {
        position: static;
        transform: none;
        max-width: none;
        flex: 1 1 auto;
        justify-content: center;
        min-width: 0;
    }


    .tabs a,
    .tabs button { padding: 8px 10px; font-size: 12.5px; }
}

/* Scenes and keepsakes: fixed column counts by width, two per row on phones. */
@media (max-width: 1080px) { .scene-grid { column-count: 4; } }
@media (max-width: 820px) { .scene-grid { column-count: 3; } }
@media (max-width: 560px) { .scene-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* Edit profile: a page of its own, not a floating box. Sizes follow the design mock. */
.profile-edit { max-width: 640px; margin: 0 auto; padding: clamp(26px, 5vw, 48px) 20px 80px; }

.profile-edit-back {
    display: flex;
    align-items: center;
    gap: 6px;
    border: 0;
    background: transparent;
    color: var(--muted);
    font: inherit;
    font-size: 13px;
    padding: 0;
    margin-bottom: 22px;
    cursor: pointer;
}

.profile-edit-back:hover { color: var(--ink); }

.profile-edit h1 {
    font-family: var(--serif);
    font-size: 32px;
    font-weight: 400;
    line-height: 1.1;
    margin: 0 0 30px;
}

.profile-edit-note { font-size: 12.5px; color: var(--muted); margin: 0 0 12px; }

/* First label sits right under the heading, so it does not need the usual top gap. */
.profile-edit > .field-label:first-of-type { margin-top: 0; }

.profile-edit .share-row { margin-top: 26px; }
.profile-edit .share-row .btn { flex: 0 0 auto; }

@media (max-width: 560px) {
    .profile-edit h1 { font-size: 26px; }
    .profile-edit .field-pair { grid-template-columns: 1fr; }
}

/* The face cluster opens the full list of who joined, so it has to look tappable. */
.event-guests.can-open { cursor: pointer; border-radius: 999px; }
.event-guests.can-open:hover .event-count { color: var(--ink); }

.modal.guest-list {
    max-width: 460px;
    max-height: 86vh;
    background: #F7F6F3;
    border-radius: 18px;
    display: flex;
    flex-direction: column;
    padding: 0;
}

.guest-list-head {
    display: flex;
    align-items: flex-start;
    gap: 14px;
    padding: 20px 20px 14px;
    border-bottom: 1px solid rgba(18, 22, 26, .08);
}

.guest-list-head > div { flex: 1 1 auto; min-width: 0; }
.guest-list-head h2 { font: 400 21px/1.25 var(--serif); margin: 4px 0 0; }

.guest-list-close {
    flex: 0 0 auto;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    border: 1px solid rgba(18, 22, 26, .14);
    background: transparent;
    color: #5F6469;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}

.guest-list-body { overflow-y: auto; padding: 8px; }

.guest-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 10px;
    border-radius: 12px;
    color: inherit;
    text-decoration: none;
}

a.guest-row:hover { background: rgba(18, 58, 107, .05); }

.guest-row .face {
    flex: 0 0 auto;
    width: 42px;
    height: 42px;
    border-radius: 50%;
    overflow: hidden;
    background: #E8E5DF;
    display: flex;
    align-items: center;
    justify-content: center;
    font: 400 17px/1 var(--serif);
    color: rgba(18, 22, 26, .45);
}

.guest-row .face img { width: 100%; height: 100%; object-fit: cover; }
.guest-row .lines { flex: 1 1 auto; min-width: 0; }
.guest-row .lines b { display: block; font-size: 14.5px; font-weight: 500; }
.guest-row .lines em { font-style: normal; font-size: 12.5px; color: var(--muted); }
.guest-row .go { flex: 0 0 auto; color: #9A9EA3; display: flex; }

/* Nobody in the album yet: the row is a plain line, so the arrow must not promise a link. */
.guest-row.is-plain { cursor: default; }
.guest-row.is-plain .go { opacity: 0; }

/* Phones: the blue date field turns into a thin strip on top so it stops eating a third of the card. */
@media (max-width: 720px) {
    .event-card { flex-direction: column; }

    .event-when {
        flex: 0 0 auto;
        flex-direction: row;
        align-items: baseline;
        justify-content: flex-start;
        gap: 8px;
        min-height: 0;
        padding: 12px 16px;
    }

    .event-when b { font-size: 23px; }
    .event-when .month { white-space: nowrap; }
    .event-when .time { margin-left: auto; }

    .event-past .event-when { border-right: 0; border-bottom: 1px solid rgba(18, 58, 107, .16); }

    .event-body { padding: 16px; }
    .event-body h3 { font-size: 20px; }

    /* The floating button was sitting on top of the last card. */
    .event-fab { bottom: calc(72px + env(safe-area-inset-bottom) + 14px); }
}

/* Home card, upcoming meet-up: the place is a map link. The pin must keep its size in a tight
   column, so only the text is allowed to shrink and clip. */
.home-event .when .event-map { display: inline-flex; align-items: center; gap: 5px; max-width: 100%; color: #123A6B; text-decoration: none; }
.home-event .when .event-map svg { flex: 0 0 auto; }
.home-event .when .event-map span,
.home-event .when > span { display: block; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.home-event .when .event-map:hover span { text-decoration: underline; }

/* --- Guide page --- */

/* Phone-first: the class reads this on a phone, so the screenshots are phone-shaped and the text
   sits above them. From 900px the pair turns into two columns, text left, screen right. */
.guide { max-width: 1280px; margin: 0 auto; padding: clamp(26px, 4vw, 46px) clamp(16px, 4vw, 40px) 70px; }

.guide-head { text-align: center; margin: 0 auto clamp(30px, 4vw, 48px); }
.guide-head h1 { font: 400 clamp(30px, 6vw, 46px)/1.12 var(--serif); margin: 0 0 14px; letter-spacing: -.01em; }
.guide-head p { font-size: 15px; line-height: 1.75; color: var(--ink-soft); margin: 0 0 22px; }
.guide-jump { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; }

.guide-part { margin-bottom: clamp(40px, 5vw, 64px); scroll-margin-top: 80px; }

.guide-part-head { border-top: 1px solid var(--line); padding-top: 22px; margin-bottom: clamp(22px, 3vw, 34px); }
.guide-kicker { font-size: 11px; letter-spacing: .18em; text-transform: uppercase; color: var(--muted); }
.guide-part-head h2 { font: 400 clamp(24px, 3.4vw, 34px)/1.2 var(--serif); margin: 8px 0 10px; }
.guide-part-head p { font-size: 14px; line-height: 1.7; color: var(--ink-soft); margin: 0; max-width: 70ch; }

.guide-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: 14px; }

.guide-step {
    background: var(--card);
    border: 1px solid rgba(18, 22, 26, .09);
    border-radius: 18px;
    padding: clamp(18px, 2.4vw, 26px);
    display: flex;
    flex-direction: column;
    gap: 18px;
}

.guide-step-body { min-width: 0; }

/* The step number is a quiet marker, not a badge: the class is reading a page, not filling a form. */
.guide-num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background: var(--accent);
    color: #fff;
    font: 400 15px/1 var(--serif);
    margin-bottom: 12px;
}

.guide-step h3 { font: 400 clamp(19px, 2.2vw, 23px)/1.3 var(--serif); margin: 0 0 8px; }
.guide-step p { margin: 0; font-size: 14px; line-height: 1.75; color: var(--ink-soft); }
.guide-step p + p { margin-top: 10px; }
.guide-step b { font-weight: 500; color: var(--ink); }

/* The screenshot wears a thin phone frame so it reads as "this is your screen", without drawing a
   fake notch or a plastic device around it. */
.guide-shot { margin: 0; display: flex; justify-content: center; }

/* A caption belongs under its screenshot, not beside it. */
.guide-shot { flex-wrap: wrap; }

.guide-shot figcaption {
    flex: 1 1 100%;
    margin-top: 10px;
    text-align: center;
    font-size: 13px;
    line-height: 1.5;
    color: var(--muted);
}

/* Sub steps listed inside one big step. */
.guide-mini { margin: 0 0 12px; padding-left: 20px; }
.guide-mini li { margin-bottom: 6px; line-height: 1.65; }

.guide-shot img {
    width: min(100%, 300px);
    height: auto;
    border-radius: 20px;
    border: 1px solid rgba(18, 22, 26, .14);
    box-shadow: 0 10px 30px rgba(10, 12, 14, .12);
    background: var(--sheet);
}

.guide-note {
    margin-top: 16px;
    background: var(--sheet);
    border: 1px solid var(--line);
    border-radius: 16px;
    padding: clamp(18px, 2.4vw, 24px);
}

.guide-note h3 { font: 400 19px/1.3 var(--serif); margin: 0 0 10px; }
.guide-note p { margin: 0; font-size: 14px; line-height: 1.75; color: var(--ink-soft); }
.guide-note p + p { margin-top: 10px; }

.guide-foot { text-align: center; border-top: 1px solid var(--line); padding-top: 26px; }
.guide-foot p { font-size: 14px; color: var(--muted); margin: 0 0 14px; }

@media (min-width: 900px) {
    .guide-step { flex-direction: row; align-items: center; gap: clamp(24px, 3vw, 40px); }
    .guide-step-body { flex: 1 1 auto; }
    .guide-shot { flex: 0 0 auto; }
    .guide-shot img { width: 240px; }
}

/* The bottom bar covers the last card on phones, same as every other page. */
@media (max-width: 879px) {
    .guide { padding-bottom: calc(72px + env(safe-area-inset-bottom) + 24px); }
}

/* Two screens side by side for a step that covers two places at once. */
.guide-shot.pair { gap: 12px; flex-wrap: wrap; }
.guide-shot.pair img { width: min(46%, 190px); }
.guide-shot.wide { margin-top: 20px; }

@media (min-width: 900px) {
    .guide-shot.pair { flex-wrap: nowrap; }
    .guide-shot.pair img { width: 190px; }
}

/* The summary poster the class shares on Zalo. It sits inside the guide as the short version:
   whoever does not want to read the steps can look at one picture instead. */
.guide-poster {
    margin: 0 0 14px;
    background: var(--card);
    border: 1px solid rgba(18, 22, 26, .09);
    border-radius: 18px;
    padding: clamp(14px, 2vw, 20px);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
}

.guide-poster img { width: min(100%, 420px); height: auto; border-radius: 12px; }

/* --- Summary card written as text ------------------------------------------
   This replaces a drawn poster: adding or dropping a step is a code edit, and anyone who wants a
   card to share just screenshots this block. The width follows a portrait phone so one shot fits. */

.paper-card {
    width: min(100%, 420px);
    margin: 0 auto 10px;
    padding: clamp(20px, 3vw, 28px);
    background: var(--sheet);
    border: 1px solid rgba(18, 22, 26, .12);
    border-radius: 18px;
    box-shadow: 0 12px 32px rgba(10, 12, 14, .08);
}

.paper-head { text-align: center; padding-bottom: 16px; border-bottom: 1px solid rgba(18, 22, 26, .1); }

.paper-kicker {
    display: block;
    font-size: 11.5px;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--muted);
}

.paper-head h3 { margin: 8px 0 6px; font: 400 clamp(21px, 2.4vw, 26px)/1.25 var(--serif); color: var(--ink); }
.paper-head p { margin: 0; font-size: 13.5px; color: var(--muted); }

.paper-list { margin: 16px 0 0; padding: 0; list-style: none; counter-reset: paper; }

.paper-list li {
    position: relative;
    margin-bottom: 14px;
    padding-left: 34px;
    counter-increment: paper;
}

.paper-list li::before {
    content: counter(paper);
    position: absolute;
    left: 0;
    top: 1px;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: #123A6B;
    color: #fff;
    font-size: 12.5px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.paper-list li b { display: block; font-weight: 500; color: var(--ink); font-size: 15px; }
.paper-list li span { display: block; margin-top: 2px; font-size: 13px; line-height: 1.55; color: var(--muted); }

.paper-foot {
    margin: 18px 0 0;
    padding-top: 14px;
    border-top: 1px solid rgba(18, 22, 26, .1);
    text-align: center;
    font-size: 12.5px;
    color: var(--muted);
}

.guide-paper-note { margin: 0 0 16px; text-align: center; font-size: 13px; color: var(--muted); }
.guide-poster figcaption { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 10px 14px; text-align: center; }
.guide-poster figcaption p { margin: 0; font-size: 13.5px; color: var(--muted); }

/* --- Photographer folder -----------------------------
   Files the photographer dropped into a shared folder. They are not part of the class library
   yet, so this tab carries no hearts, no names on faces and no batch download. */
.drive-wrap {
    max-width: 1280px;
    margin: 0 auto;
    padding: clamp(18px, 2.6vw, 30px) clamp(16px, 4vw, 40px) 72px;
}

.drive-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: 18px;
    padding-bottom: 14px;
    margin-bottom: clamp(14px, 1.6vw, 20px);
    border-bottom: 1px solid rgba(18, 22, 26, .14);
}

.drive-head-text { flex: 1 1 620px; min-width: 0; }
.drive-head-text .eyebrow { font-size: 11px; letter-spacing: .2em; text-transform: uppercase; color: #5F6469; margin-bottom: 7px; }
.drive-head-text h1 { font: 400 clamp(27px, 3.4vw, 40px)/1.05 var(--serif); margin: 0 0 8px; letter-spacing: -.02em; }
.drive-head-text p { margin: 0; font-size: 14.5px; line-height: 1.7; color: var(--ink-soft); }

.drive-count { flex: 0 0 auto; }
.drive-count b { display: block; font: 400 30px/1 var(--serif); color: var(--accent); }
.drive-count span { display: block; font-size: 11.5px; color: #5F6469; margin-top: 6px; }

/* What happens to a photo once kept. Stated up front, before anyone presses the button. */
.drive-note {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    background: #F3EFE7;
    border-radius: 12px;
    padding: 11px 15px;
    margin-bottom: clamp(13px, 1.6vw, 18px);
}

.drive-note svg { flex: 0 0 auto; margin-top: 1px; stroke: #8A6A2F; }
.drive-note span { font-size: 13px; line-height: 1.65; color: #5A5245; }

/* Columns rather than a row grid: a folder straight from a camera holds every ratio, and rows
   with a locked height leave white holes under the landscape cells. */
.drive-grid { column-count: 5; column-gap: clamp(10px, 1.2vw, 18px); }

.drive-cell {
    position: relative;
    background: rgba(18, 22, 26, .06);
    border-radius: 4px;
    overflow: hidden;
    break-inside: avoid;
    margin-bottom: clamp(10px, 1.2vw, 18px);
}

.drive-open {
    display: block;
    width: 100%;
    height: 100%;
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
}

.drive-cell img { display: block; width: 100%; height: 100%; object-fit: cover; }


.drive-foot {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 12px;
    padding-top: clamp(16px, 2vw, 26px);
}

.drive-shown { font-size: 12.5px; color: var(--muted); }
.drive-more { min-height: 46px; padding: 0 26px; border-radius: 999px; }

/* No folder plugged in yet. */
.drive-empty { max-width: 620px; margin: clamp(40px, 9vw, 90px) auto; text-align: center; }

.drive-empty-icon {
    width: 60px;
    height: 60px;
    border-radius: 50%;
    border: 1px solid rgba(18, 22, 26, .16);
    margin: 0 auto 22px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--accent);
}

.drive-empty h2 { font: 400 26px/1.25 var(--serif); margin: 0 0 12px; }
.drive-empty p { margin: 0; font-size: 14px; line-height: 1.7; color: var(--muted); }


@media (max-width: 1099px) { .drive-grid { column-count: 4; } }
@media (max-width: 819px) { .drive-grid { column-count: 3; } }

@media (max-width: 879px) {
    /* The propose button and the birthday button sit in the bottom right corner, so the last row
       of photos needs room or they cover it. */
    .drive-wrap { padding-bottom: 96px; }
}

@media (max-width: 519px) { .drive-grid { column-count: 2; } }

/* Strip at the foot of the home page: the photographer just dropped a folder in. */
.home-drive { background: #F3EFE7; border-top: 1px solid rgba(18, 22, 26, .09); }

.home-drive-inner {
    max-width: 1280px;
    margin: 0 auto;
    padding: clamp(22px, 3.4vw, 34px) clamp(16px, 4vw, 40px);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: clamp(14px, 2vw, 28px);
}

.home-drive-text { flex: 1 1 340px; min-width: 0; }
.home-drive-tag { display: flex; align-items: center; gap: 9px; margin-bottom: 9px; }
.home-drive-tag i { width: 7px; height: 7px; border-radius: 50%; background: #B14D1B; }
.home-drive-tag span { font-size: 11px; letter-spacing: .18em; text-transform: uppercase; color: #B14D1B; font-weight: 600; }
.home-drive-title { font: 400 clamp(21px, 3vw, 28px)/1.25 var(--serif); color: var(--ink); margin-bottom: 7px; }
.home-drive-title b { font-weight: 400; }
.home-drive-text p { margin: 0; font-size: 13.5px; line-height: 1.6; color: #5F6469; max-width: 780px; }
.home-drive .btn { flex: 0 0 auto; min-height: 48px; padding: 0 24px; border-radius: 999px; }

/* While a photo is open, the floating buttons would sit on top of the action row. */
body.is-viewing .birthday-fab,
body.is-viewing #eventOpenForm { display: none !important; }

/* Page bottoms on a phone. The 70px tail was meant for wide screens; on a phone the bottom bar
   adds its own room on top of it and the page ends on a tall band of empty ground. */
@media (max-width: 719px) {
    section[data-panel] { padding-bottom: 24px; }
    .home-block.last { padding-bottom: 20px; }
    .profile { padding-bottom: 24px; }
    .page-head { padding-bottom: 6px; }
    .profile-tabs { margin-top: 14px; }
    .grid-more { padding-top: 22px; }
}

/* The visitor group in the identity picker. It holds a single cell and no face, so it stays
   tucked to the left instead of stretching across a whole row of people. */
.claim-group.is-visitor .claim-group-grid { display: block; }

.claim-group.is-visitor .face-card {
    display: flex;
    align-items: center;
    gap: 12px;
    text-align: left;
    max-width: 420px;
    padding: 14px 16px;
    border: 1px solid rgba(18, 22, 26, .14);
    border-radius: 8px;
    background: #fff;
}

.claim-group.is-visitor .face-card:hover { border-color: rgba(18, 58, 107, .4); }

.claim-group.is-visitor .face-card .photo {
    width: 38px;
    height: 38px;
    flex: 0 0 38px;
    margin: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #123A6B;
}

.claim-group.is-visitor .face-card .name { font-size: 13.5px; }

/* Lead line shown while the reader is browsing as a visitor. */
.claim-visitor-note {
    /* Same frame as the picker below it. Left free, the line runs the full width of the screen
       while everything around it is indented, and it reads as a stray band across the page. */
    max-width: calc(1280px - 2 * clamp(16px, 4vw, 40px));
    width: calc(100% - 2 * clamp(16px, 4vw, 40px));
    margin: 0 auto 18px;
    padding: 12px 14px;
    border-radius: 8px;
    background: rgba(18, 58, 107, .05);
    border: 1px solid rgba(18, 58, 107, .12);
    font-size: 13.5px;
    line-height: 1.6;
    color: #3B4045;
}

/* --- Video tab ------------------------------------------------------------
   Films are hosted on the class server. A card loads the cover only; the film itself waits
   for a tap, see openVideo in photos.js. */
.video-wrap { padding-bottom: 96px; }

/* One film to a row. The class has a handful of films, not a wall of them, so a wide card that
   shows the cover properly reads better than three small ones side by side. The row is capped so
   the picture does not stretch across a whole desktop screen. */
.video-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(18px, 2.4vw, 34px);
    max-width: 880px;
    margin: 0 auto;
}

.video-cell {
    display: block;
    width: 100%;
    text-align: left;
    background: none;
    border: 0;
    padding: 0;
    font-family: inherit;
    color: inherit;
    cursor: pointer;
}

.video-thumb {
    position: relative;
    display: block;
    aspect-ratio: 16 / 9;
    border-radius: 6px;
    overflow: hidden;
    background: #E7E4DE;
}

.video-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* A thin scrim so the play button stands out from a bright cover picture. */
.video-thumb::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(to top, rgba(10, 11, 12, .34), rgba(10, 11, 12, .04));
}

.video-play {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 54px;
    height: 54px;
    border-radius: 50%;
    background: rgba(255, 255, 255, .94);
    color: #123A6B;
    box-shadow: 0 6px 20px rgba(10, 20, 35, .24);
}

.video-cell:hover .video-play { background: #fff; }

.video-time {
    position: absolute;
    right: 8px;
    bottom: 8px;
    z-index: 1;
    padding: 2px 7px;
    border-radius: 5px;
    background: rgba(10, 11, 12, .62);
    color: #fff;
    font-size: 11.5px;
    letter-spacing: .02em;
}

.video-name {
    display: block;
    margin-top: 10px;
    font: 400 16px/1.35 var(--serif);
}

.video-size { display: block; margin-top: 2px; font-size: 12px; color: var(--muted); }

/* Which meet-up the film came from. Only drawn while every day is on screen at once. */
.video-event { display: block; margin-top: 2px; font-size: 13px; color: var(--muted); }

/* The row that picks a meet-up. Scrolls sideways on a phone rather than wrapping into a block
   that pushes the films off the first screen. */
.video-filters {
    display: flex;
    gap: 8px;
    max-width: 880px;
    margin: 0 auto 22px;
    padding-bottom: 4px;
    overflow-x: auto;
    scrollbar-width: none;
}

.video-filters::-webkit-scrollbar { display: none; }

.video-filter {
    flex: 0 0 auto;
    padding: 7px 14px;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: transparent;
    color: var(--muted);
    font: 400 14px/1.2 var(--sans);
    cursor: pointer;
}

.video-filter:hover { color: var(--ink); border-color: var(--ink); }

.video-filter.is-on {
    background: var(--ink);
    border-color: var(--ink);
    color: #fff;
}

/* The player, on the same ground as the photo viewer. */
.video-viewer .viewer-stage { display: flex; align-items: center; justify-content: center; }

.video-viewer-title { font: 400 15px/1.3 var(--serif); color: #fff; }

.video-frame {
    width: min(100%, 1180px);
    aspect-ratio: 16 / 9;
    background: #000;
}

.video-frame video { width: 100%; height: 100%; display: block; }

.video-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    padding: 14px clamp(14px, 3vw, 26px);
}

/* The pale outlined button is made for light ground and disappears on the dark player. */
.video-foot .btn-ghost {
    border-color: rgba(255, 255, 255, .34);
    color: #fff;
    text-decoration: none;
}

.video-foot .btn-ghost:hover { border-color: #fff; background: rgba(255, 255, 255, .08); }

.video-note { font-size: 12.5px; color: rgba(255, 255, 255, .6); }

/* --- Legal pages: privacy policy and terms of service --- */

/* Plain reading pages. One narrow column so lines stay short on a laptop, and the same paper tone
   as the rest of the site. The two app stores link straight here, so the page must look finished
   on a phone with no login. */
.legal { max-width: 820px; margin: 0 auto; padding: clamp(26px, 4vw, 46px) clamp(16px, 4vw, 40px) 70px; }

.legal-head { border-bottom: 1px solid var(--line); padding-bottom: clamp(18px, 2.4vw, 26px); margin-bottom: clamp(24px, 3vw, 34px); }
.legal-head h1 { font: 400 clamp(28px, 5vw, 42px)/1.15 var(--serif); margin: 8px 0 10px; letter-spacing: -.01em; }
.legal-date { font-size: 13px; color: var(--muted); margin: 0; }

.legal-body { font-size: 15px; line-height: 1.8; color: var(--ink-soft); }
.legal-lead { font-size: 16px; color: var(--ink); margin: 0 0 clamp(22px, 3vw, 32px); }

.legal-body h2 { font: 400 clamp(20px, 2.6vw, 25px)/1.3 var(--serif); color: var(--ink); margin: clamp(28px, 3.4vw, 38px) 0 10px; }
.legal-body h3 { font: 600 15px/1.4 var(--sans); color: var(--ink); margin: 22px 0 6px; }
.legal-body p { margin: 0 0 14px; }
.legal-body ul,
.legal-body ol { margin: 0 0 14px; padding-left: 20px; }
.legal-body li { margin-bottom: 8px; }
.legal-body strong { color: var(--ink); font-weight: 600; }

.legal-link { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }

.legal-foot { display: flex; flex-wrap: wrap; gap: 10px; border-top: 1px solid var(--line); margin-top: clamp(32px, 4vw, 46px); padding-top: clamp(20px, 2.6vw, 28px); }

/* Site footer. The two legal pages used to sit in the account menu and in the More sheet; both
   app stores point straight at them, so they now live here where a signed-out visitor sees them
   too. On phones the bottom tab bar floats over the page, hence the extra room underneath. */
/* Không có lề trên: khối cuối mỗi trang đã tự chừa đuôi của nó rồi, cộng thêm lề ở đây là ra
   một dải trống dài trước chân trang. Riêng khối Khoảnh khắc chung còn có nền và viền riêng nên
   chân trang dán thẳng vào là vừa. */
/* Dải mời cài ứng dụng cho khách dùng iPhone, iPad hay Android. Nằm trên thanh đầu trang và
   cuộn đi cùng trang. */
.app-hint {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px clamp(12px, 4vw, 40px) 10px 8px;
    background: var(--card);
    border-bottom: 1px solid var(--line);
}

/* Máy tự khai là Mac thì máy chủ dựng sẵn dải nhưng giấu đi; chỉ máy dùng ngón tay, tức iPad,
   mới mở ra. Giấu bằng bảng kiểu chứ không bằng mã: mã chạy sau nhịp vẽ đầu là trang xô lệch. */
.app-hint[data-hoi-man-cham] { display: none; }

@media (pointer: coarse) {
    .app-hint[data-hoi-man-cham] { display: flex; }
}

.app-hint-close {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: var(--muted);
    cursor: pointer;
}

.app-hint-icon { flex: 0 0 auto; width: 44px; height: 44px; border-radius: 10px; }
.app-hint-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.app-hint-name { font: 500 14px/1.2 var(--sans); color: var(--ink); }
/* Hai dòng chữ giữ đúng MỘT dòng mỗi dòng: gãy dòng là dải cao gấp đôi, đẩy cả trang xuống. */
.app-hint-name,
.app-hint-note { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.app-hint-note { font-size: 12px; color: var(--muted); }

.app-hint-go {
    flex: 0 0 auto;
    padding: 7px 16px;
    border-radius: 999px;
    background: var(--accent);
    color: #fff;
    font: 500 13px/1 var(--sans);
    text-decoration: none;
}

.site-foot { border-top: 1px solid var(--line); padding: clamp(18px, 2.2vw, 24px) 0 calc(clamp(18px, 2.2vw, 24px) + env(safe-area-inset-bottom)); }
.site-foot-inner { max-width: 1280px; margin: 0 auto; padding: 0 clamp(16px, 4vw, 40px); display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }
/* Tên lớp bám mép trái, hai đường pháp lý bám mép phải: auto ở giữa đẩy hai bên ra hai đầu,
   chắc hơn space-between vì dòng chữ nào rỗng cũng không kéo bên kia trôi vào giữa. */
.site-foot-name { font-size: 12.5px; color: var(--muted); margin-right: auto; }
/* Hai tấm huy hiệu của kho ứng dụng đứng giữa chân trang: tên lớp đẩy sang trái, hai đường pháp
   lý đẩy sang phải, khoảng auto hai bên nhả chỗ ở giữa cho khối này.

   🔴 Bề cao khai bằng CSS còn bề ngang để tự tính. Hai tấm khác tỉ lệ nhau, ép cùng một bề ngang
   là méo hình chính chủ. Thẻ ảnh vẫn khai đủ số đo GỐC nên trình duyệt giữ sẵn chỗ, không xô lệch
   bố cục lúc tấm về. */
.site-foot-apps { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.site-foot-app { display: block; line-height: 0; border-radius: 8px; }
.site-foot-app img { display: block; height: 40px; width: auto; }
.site-foot-app:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.site-foot-links { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px 18px; margin-left: auto; }
.site-foot-links a { font-size: 12.5px; color: var(--muted); text-decoration: none; }
.site-foot-links a:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }

@media (max-width: 860px) {
    /* Thanh thẻ dưới đáy nổi đè lên trang, chừa chỗ để chân trang không nằm dưới nó. */
    .site-foot { padding-bottom: calc(84px + env(safe-area-inset-bottom)); }
    /* Khổ hẹp thì cả ba khối xếp chồng và canh giữa. Hai lề auto của tên lớp và hàng pháp lý phải
       gỡ bỏ, không thì chúng vẫn tự đẩy về hai đầu và khối giữa lệch theo. */
    .site-foot-inner { flex-direction: column; align-items: center; text-align: center; }
    .site-foot-name { margin-right: 0; }
    .site-foot-links { margin-left: 0; justify-content: center; }
}

/* Space kept for content that only arrives once the script has answered.

   The video wall and the photographer's folder both start out as an empty box, so the footer sat
   right under the heading and then dropped a whole screen when the cells landed: measured as a
   layout shift of 0.31 on the shared moments page and 0.35 on the video page. Holding a screenful
   keeps the footer below the fold, where a later change costs nothing.

   🔴 The screenful has to be held for good, not only while the box is empty. Releasing it once the
   cells land pulls a short wall back up the page and the footer jumps into view instead: that read
   as 0.30 on the wide screen, worse than the shift it replaced. */
.drive-wrap,
.video-wrap { min-height: 100vh; }
