/* attract screen: title, subtitle and the rule are defined ONCE in
   framework/kiosk.css now. The override that sat here made this station
   look different from its five neighbours for no reason anyone chose.
   Only the Devanagari line-height tuning stays local, because the strings
   differ. See the attract block in kiosk.css. */
/* Oral History Interview Station app-specific styles.
   Design system lives in ../../framework/kiosk.css; only additions here.
   Target: 32" ViewSonic TD3207, 1920x1080 landscape, touch. */

.hidden { display: none !important; }

/* ==========================================================================
   LOCAL OVERRIDES OF SHARED FRAMEWORK RULES
   These compensate for defects in ../../framework/kiosk.css. Each one is
   reported upstream for a central fix; delete the block once the framework
   carries the fix, otherwise leave it alone.
   ========================================================================== */

/* [FW-1] Ghost taps from inactive screens.
   kiosk.css does `.screen { pointer-events: none }` but then, later and with
   equal weight, `button, .touchable { pointer-events: auto }`. Descendants win,
   so every button on a FADED-OUT screen stays hit-testable. #profile follows
   #home in the DOM and both are inset:0, so the invisible profile's clip rows
   sit on top of the home grid: tapping subject card 2/3/4 fired the PREVIOUS
   subject's recording. Kill hit-testing on the whole inactive subtree, and
   hide it from focus/AT once the cross-fade has finished. */
.screen:not(.active),
.screen:not(.active) * { pointer-events: none !important; }
.screen:not(.active) { visibility: hidden; transition: opacity .45s ease, visibility 0s linear .45s; }
.screen.active       { visibility: visible; transition: opacity .45s ease, visibility 0s; }
#saver.hidden, #saver.hidden * { pointer-events: none !important; }

/* [FW-2] was a set of local repairs to `.chip`: a transparent fill so
   unselected chips did not render as user-agent grey pills, 52px raised to
   64px, and an ellipsis on over-long labels. All three are gone with the chip
   bar itself ([OH1]), and the framework now carries the 64px minimum for every
   app (F4), so nothing here needs to say it again. */

/* [FW-3] `.seek` is `height: 8px; padding: 20px 0; background-clip: content-box`
   under the global `box-sizing: border-box`, so the content box collapses to
   ZERO height: no track, no progress fill, nothing. Restate it with the padding
   inside a 64px target so an 8px bar is actually painted. */
#pSeek { height: var(--touch); padding: 28px 0; }
#pSeek .fill { top: 28px; bottom: 28px; }

/* ---------- screensaver ---------- */
/* Static woven texture, a no-JS FALLBACK only. The live idle backdrop is the
   shared framework loom (framework/weave-saver.js), one consistent wallpaper
   across all six gallery apps. Its opaque canvas is inserted after this div
   and fully covers it, so this paints only if that script ever fails to run.
   The old `weavedrift` animation is retired: it burned compositor time every
   idle hour behind an opaque canvas, and diverged from the gallery-wide saver. */
#saver .weavebg {
  position: absolute; inset: 0; opacity: .16; pointer-events: none;
  background:
    repeating-linear-gradient(90deg, var(--gold) 0 2px, transparent 2px 28px),
    repeating-linear-gradient(0deg, var(--madder) 0 2px, transparent 2px 36px);
}


/* ==========================================================================
   [D1] THE BILINGUAL ATTRACT SCREEN
   The title, the one line and the action are painted in English AND Hindi at
   once, same size, same weight, Hindi under English, so a Hindi-first visitor
   walking up to an idle station reads their own language without first
   finding and pressing a control.

   The stacking, and the Devanagari face for the Hindi span, belong to the
   framework (F1) and are NOT restated here. The one thing left to the app is
   leading: h1 is set at line-height 1.15 for Latin display type, and at 4.2em
   that shaves the matras off the top of the Devanagari line stacked under it.
   ========================================================================== */
#saver [data-i18n-both] > [lang="hi"] { line-height: 1.4; }
#saver h1[data-i18n-both] > [lang="hi"] { margin-top: .1em; }

/* ==========================================================================
   [LANG-1] KEEPING CLEAR OF THE LANGUAGE TOGGLE
   The framework's .langpill is `position: fixed` in the top-left corner and
   paints above this app's content on every screen. Two of this app's own
   controls were sitting in exactly that spot and were being covered by it
   (measured at 1920x1080 against the pill of the day, 20,20 -> 224,85):

     #home  the topbar starts at x=32,y=22, so the pill was painted straight
            across "Voices of the Loom" / "करघे की आवाज़ें".
     #profile  .backbtn is `top: 1em; left: 1em` in kiosk.css, i.e. the same
            coordinates: "‹ All voices" was completely hidden, and a touch
            aimed at it landed on the toggle instead and silently switched
            the kiosk's language. testing/test_oral_history.py reproduced
            this as five separate failures before the fix.

   Reserve the corner once, in one variable, so both screens move together and
   a future change to the pill's size is a one-line edit. The clearance is the
   pill's measured right edge plus a finger's worth of gap; the pill is the
   same width in both languages because both labels are always shown.

   [D2/F3] Re-measured against the framework's 64px pill. The pill stopped
   being treated as a rarely-used preference: for a Hindi-first visitor it is
   the most important control on the screen, so both labels are now a full
   64px target at body size.

   [D8] The hardcoded 240px is GONE. The corner is no longer the language
   toggle alone: the framework now parks a settings CLUSTER there (language
   plus the three-step type size), measured at 17,17 -> 427,91 on this panel,
   and it publishes its own real clearance on the documentElement and keeps it
   current across a language switch, a type-size change and the moment the
   Devanagari faces finish loading. An app guessing its own number is an app
   that is wrong the next time that chrome changes width. --langpill-clear is
   still published as an alias but it is deprecated, so this file asks for the
   real one. The literal fallback is only for the single frame before kiosk.js
   has measured anything.

   #profile .backbtn no longer needs a rule at all: kiosk.css moves
   `.screen .backbtn` to the same variable for every app. It is kept as an
   explicit statement of intent, at the same value, so deleting the framework
   rule cannot silently drop this app's back button under the cluster again.
   ========================================================================== */
#profile .backbtn { left: var(--kiosk-chrome-clear, 451px); }

/* The station title used to be pushed 451px to the right to get out from
   under that cluster, which left it floating in the middle of the top edge
   with nothing beneath it lining up. It now sits BELOW the chrome instead,
   hard against the same left margin as the first card, so the title, the
   subtitle and the grid share one edge. --kiosk-chrome-bottom is the
   cluster's measured bottom plus a gap, published by the framework for
   exactly this. */
#home .topbar {
  padding: var(--kiosk-chrome-bottom, 107px) 2.2em .9em 2.2em;
  align-items: flex-end;
}

/* [LANG-1b] The local lift of the toggle above the screensaver is gone: the
   framework now paints .langpill at z-index 110 for all six stations (F2), so
   a Hindi-first visitor can choose their language on an idle screen without
   every app repeating the same patch. */
/* #pClose keeps kiosk.css's top-left corner and does not need clearance: the
   framework drops the toggle to z-index 50 for as long as a recording is
   playing, precisely so a film's close button is never covered by it. */

/* ---------- home / topbar ----------
   [D8] The title is the display face, and it is now genuinely a size apart
   from a card caption rather than a half-step. The old 2.2em h2 sat close
   enough to the 1.24em card headings that the screen read as a list of
   equals with a slightly bigger line on top. The framework's h1 is 3.4em;
   this is an h2 in the markup because the app title is not the document
   title, so the size is stated here. */
.apptitle {
  font-family: var(--display); font-weight: 400;
  font-size: 3.05em; line-height: 1.04; letter-spacing: .008em;
  color: var(--ink);
}
/* the ‹ / ⟲ glyphs sit outside the translated <span> so that the framework's
   textContent swap on [data-i18n] cannot eat them */
#profileBack .glyph, #pReplay .glyph { flex: 0 0 auto; }

/* ==========================================================================
   [OH6] THE SUBJECT GRID: FIVE BOLTS OF CLOTH
   Four columns left the fifth craft alone on a second row with three empty
   holes beside it, and the whole grid occupied the top half of a 1920x1080
   panel: 40 per cent of the screen was unused, and the used part was five
   16:9 boxes of the same grey placeholder.

   Five columns, one row, each card the full remaining height of the panel.
   A tall narrow card is not an arbitrary shape here: it is a length of cloth
   hanging on a wall, which is what the visitor is standing in front of and
   what the card now actually contains. The card fills the panel top to
   bottom, so there is no dead third left to fill.

   minmax(0,1fr) on the columns AND on the rows, not 1fr: a bare 1fr carries
   an auto minimum, so a craft name long enough to wrap six times would push
   its row taller than the panel and put the grid into a scroll it does not
   need. The columns are capped the same way so an unbreakable string cannot
   widen one column past its share (pinned: every column keeps the same
   width). If the gallery team ever adds a sixth craft the row simply splits
   into two shorter rows rather than overflowing.
   ========================================================================== */
#subjectGrid {
  grid-template-columns: repeat(5, minmax(0, 1fr));
  grid-auto-rows: minmax(0, 1fr);
  align-content: stretch;
  padding: 0 2.2em 2.2em;
  gap: 1.3em;
}
@media (orientation: portrait) {
  #subjectGrid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* The cloth IS the card. The thumb is taken out of flow and made the card's
   whole ground instead of a 16:9 picture with a filled caption bar bolted
   underneath it, and the caption is set on the weave itself. That removes the
   two things that made these read as the default card component: the separate
   filled meta block, and the hard rectangle of border around a picture of
   nothing.
   [VK1] 17 Aug: also targets .groupgrid .card, the three Vishwakarma expert
   cards added below. Widening the SELECTOR to a second grid does not change
   a single declaration these rules already made for #subjectGrid, so the
   five craft cards are provably unaffected (screenshot-proved); it only
   asks the same already-approved "cloth is the card" language for the new
   section instead of reverting to the framework's default boxed card,
   which this app deliberately moved away from ([OH6] below). */
#subjectGrid .card, .groupgrid .card { position: relative; }
#subjectGrid .card .thumb, .groupgrid .card .thumb {
  position: absolute; inset: 0;
  width: 100%; height: 100%; aspect-ratio: auto;
  object-fit: cover; background: #16130f;
}
/* The caption band, and the only one: app.js deliberately does NOT ask the
   renderer for a scrim as well, because two ramps stacked took the bottom two
   thirds of every card to near black. Doing it here is also what lets the
   ramp start where the caption actually starts. Ten patterns and eight
   palettes mean the value under the caption is not the same on every card,
   and a caption that is legible on four cards out of five is a defect on a
   wall nobody staffs. This gradient does not depend on which bolt came up. */
#subjectGrid .card::after, .groupgrid .card::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg,
              rgba(16,13,10,0) 46%, rgba(16,13,10,.14) 62%,
              rgba(16,13,10,.72) 79%, rgba(16,13,10,.95) 100%);
}
#subjectGrid .card .meta, .groupgrid .card .meta {
  position: relative; z-index: 1;
  margin-top: auto;            /* NOT justify-content: a flex end-alignment
                                  makes overflowing content unreachable */
  flex: 0 0 auto;
  padding: 1em 1.05em 1.15em;
}
/* Solid, not light. The framework raised the hierarchy by inverting weight
   against the display face, and that inversion is what is wanted here too:
   the title above is big and open, the label on the cloth is smaller and
   set solid, which is what survives being read across a woven ground. The
   shadow is for the same reason and costs nothing. */
#subjectGrid .card .meta h3, .groupgrid .card .meta h3 {
  font-size: 1.62em; line-height: 1.16; margin-bottom: .12em;
  color: var(--ink); text-shadow: 0 2px 12px rgba(0,0,0,.8);
}
.card .cardfoot {
  display: block; margin-top: .5em; color: var(--gold);
  font-family: var(--sans); font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase;
  text-shadow: 0 2px 10px rgba(0,0,0,.85);
}

/* Long/unbroken names must wrap inside the card, never widen the column.
   [D6] The two-line clamp is gone: a clamp is a truncation, and a truncation
   a visitor cannot open is a screen teasing text it will not give you. Five
   craft names of one word each never came near it; if a longer one ever does,
   the card grows and the grid absorbs it. */
.card .meta h3 { overflow-wrap: anywhere; }
.card .cardfoot { overflow-wrap: anywhere; }

/* The one thing that can still take the place of the cards: the notice shown
   when content/manifest.json cannot be read at all. That is not a visitor
   state, it is gallery staff finding a station down, so it spans the grid and
   sits where the cards would have been rather than being squeezed into one
   445px column. (The empty-search state this styling was written for is gone
   with the search itself; only this use is left.) */
.noresults {
  grid-column: 1 / -1; text-align: center; padding: 4em 2em;
  display: flex; flex-direction: column; align-items: center; gap: 1.2em;
}
.noresults .body { max-width: 34em; }

/* ==========================================================================
   [VK1] "LOOKING BACK AT VISHWAKARMA", 17 Aug 2026
   A second, titled group of subjects on the SAME #home screen as the five
   crafts, reached by scrolling rather than by a tap to another screen.

   #home was a single fixed fold with no scroll at all (.screen carries no
   overflow rule, and html/body are overflow:hidden). .homeFold now holds
   exactly what #home used to hold - the topbar and #subjectGrid - pinned to
   min-height:100% and flex-shrink:0, so that wrapper can never be less than
   one full screen tall no matter what is appended after it. #home itself
   becomes the scroller. At scrollTop 0 the visitor sees precisely the old
   fold, pixel for pixel (screenshot-proved); scrolling down reveals this
   section below it. Nothing here changes #subjectGrid's own rules.
   ========================================================================== */
#home { overflow-y: auto; overscroll-behavior: contain; touch-action: pan-y; }
#home::-webkit-scrollbar { width: 0; }
.homeFold {
  display: flex; flex-direction: column;
  min-height: 100%; flex: 0 0 auto;
}

.groupSections { flex: 0 0 auto; }
.groupSection { padding: .4em 2.2em 3.4em; }
.groupHead { max-width: 64em; margin-bottom: 1.1em; }
/* Matches .apptitle's voice a size down: this is a second heading on the
   same screen, not a new screen's title, so it must read as part of the
   page rather than repeat the display size at the top. */
.grouptitle {
  font-family: var(--display); font-weight: 400;
  font-size: 2.15em; line-height: 1.14; letter-spacing: .006em;
  color: var(--ink); margin-bottom: .3em;
}
.groupintro { max-width: 52em; }
.grouptitle, .groupintro { overflow-wrap: anywhere; }

/* Three portraits, not five: the group grid gets its own column count and
   its own explicit height (the craft grid's flex:1 has nothing to fill
   here, since .groupSection is a normal block, not a flex container) rather
   than reusing #subjectGrid's rule, which stays untouched. minmax(0,1fr) on
   both axes for the same reason #subjectGrid uses it: an unbreakable name
   must not widen a column or grow a row past its share. */
.groupgrid {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-auto-rows: minmax(0, 1fr);
  height: 58vh; min-height: 420px;
  align-content: stretch;
  padding: 0; gap: 1.3em;
  overflow: visible;           /* the section already sits inside the #home
                                   scroller; a second scrollbar here is not
                                   wanted and three cards never need one */
}
@media (orientation: portrait) {
  .groupgrid { grid-template-columns: repeat(1, minmax(0, 1fr)); height: auto; }
}

html[lang="hi"] .grouptitle { line-height: 1.3; }

/* ==========================================================================
   [OH6] PROFILE: THE CLOTH IS THE WHOLE PANEL
   Before: a 460px column holding a 4:3 grey placeholder and the intro, a list
   of three filled boxes beside it, and the bottom half of a 1920x1080 panel
   empty. The picture was the smallest thing on a screen that had nothing else
   to look at.

   Now the craft's own weave is one full-bleed image behind everything, with
   the label reading off the cloth on the left and the recordings reading off
   ink on the right, the two joined by a gradient rather than by a column
   edge. Two things follow from doing it this way rather than as a 44 per cent
   picture column. The screen is full by construction, so a craft with one
   recording does not leave half a panel bare the way a craft with three does
   not. And it bleeds off the edges on purpose: cloth continues past the
   frame, a thumbnail does not, and the difference is most of what makes one
   read as an image and the other as a placeholder.

   The cloth starts BELOW the chrome, not at y=0. That is not a decoration:
   the settings cluster and the Home pill are fixed over the top corners, and
   the suite fails any app element that lands under either of them. Clearing
   them also means the ghost "All voices" button is read against flat ink
   rather than against whatever colour of yarn happened to come up under it.
   ========================================================================== */
.profilewrap {
  flex: 1; position: relative; overflow: hidden;
  display: grid; grid-template-columns: 46% 1fr; gap: 0;
  /* minmax(0,1fr) on the row, not auto. An auto row is sized to its content
     first and only stretches into space that is left over, so an intro long
     enough to exceed the panel would make the row taller than the screen and
     .profilecard would grow with it instead of scrolling. That is the exact
     property phase 11 asserts, and it is not visible until someone pastes a
     900-word bio into the manifest at 11pm. */
  grid-template-rows: minmax(0, 1fr);
  padding: var(--kiosk-chrome-bottom, 107px) 0 0;
}

.profilecloth {
  position: absolute; left: 0; right: 0; bottom: 0;
  top: var(--kiosk-chrome-bottom, 107px);
  overflow: hidden; background: #16130f;   /* the ground before cloth arrives */
}
.profilecloth > img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
}
/* Two ramps, and each has a job. The horizontal one carries the screen from
   cloth on the left to ink on the right, so the recordings list never has to
   be legible over weave. The vertical one darkens the foot of the panel where
   the craft name and its intro sit, and lifts the top where there is nothing
   but cloth to look at. Stated in CSS rather than asked of the swatch because
   .profilecard scrolls: a scrim baked into the image would not travel with
   the words that need it.

   THE VERTICAL STOPS ARE MEASURED, NOT CHOSEN BY EYE. With the ramp reaching
   only .5 at 70% the label was legible on nine screens out of ten and not on
   the tenth: sampled with every glyph hidden, so the reading is the real
   composited ground and not the type sitting on it, the gold craft line came
   out at 2.7:1 worst-pixel on Tangaliya and 3.7:1 on Ashawali, against 7.7:1
   for the same gold on flat --bg. Median was near 7 in every case, which is
   exactly how this defect hides: the line looks fine, and then one bright
   weft crosses one word. Reaching .72 by 60% and .88 by 72% puts the worst
   pixel back over 6:1 on all ten. It costs some visible weave under the
   intro, and legibility on a wall with no staff on it is worth more than
   that. Re-measure if these numbers are touched. */
.profilecloth::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(180deg, rgba(14,12,9,0) 30%, rgba(14,12,9,.22) 46%,
                    rgba(14,12,9,.72) 60%, rgba(14,12,9,.88) 72%,
                    rgba(14,12,9,.92) 100%),
    linear-gradient(90deg, rgba(14,12,9,.16) 0%, rgba(14,12,9,.2) 22%,
                    rgba(14,12,9,.78) 42%, rgba(14,12,9,.95) 54%,
                    rgba(14,12,9,.975) 100%);
}

/* Still the scroller, deliberately: phase 11 pins that an absurd intro makes
   THIS element scroll rather than burst the column, and the cloth behind it
   must not scroll with it. margin-top:auto on the first child rather than
   justify-content:flex-end, because an end-aligned flex container puts
   overflowing content above the scroll origin where it cannot be reached. */
.profilecard {
  position: relative; z-index: 1; min-height: 0;
  overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain;
  display: flex; flex-direction: column;
  padding: 1.6em 2.2em 2.6em 2.4em;
}
.profilecard > :first-child { margin-top: auto; }
.profilecard h2, .profmeta, .profilecard .body { overflow-wrap: anywhere; }
.profilecard::-webkit-scrollbar { width: 0; }
.profilecard h2 {
  font-family: var(--display); font-weight: 400; font-size: 3.4em;
  line-height: 1.04; color: var(--ink);
  text-shadow: 0 2px 16px rgba(0,0,0,.75);
}
/* .eyebrow now, not .caption: this is the line ABOVE the title, so it stops
   being a footnote set at caption size underneath it. The framework owns the
   tracking and the gold; only the floor size and the shadow are app business. */
.profmeta { margin: 0 0 .5em; text-shadow: 0 2px 10px rgba(0,0,0,.85); }
.profilecard .weave-rule { margin: 1em 0 .9em; opacity: .65; }
.profilecard .body {
  margin-top: 0; font-size: 1.05em; color: var(--ink);
  max-width: 30em; text-shadow: 0 1px 10px rgba(0,0,0,.7);
}

/* The heading and the list are centred TOGETHER, as one block. Centring only
   the list left "RECORDINGS" stranded against the top edge with 250px of
   nothing under it before the first row, which is the same disconnection the
   old layout had. `safe center` rather than plain `center`: a centred flex
   container puts overflowing content above its own scroll origin, where a
   visitor cannot reach it, and the plain line before it is the fallback for a
   Chromium that does not know the keyword. */
.clipwrap {
  position: relative; z-index: 1; min-height: 0;
  display: flex; flex-direction: column; overflow: hidden;
  justify-content: center;
  justify-content: safe center;
  padding: 1.6em 2.6em 6em 3em;
}
.cliphead { color: var(--gold); margin-bottom: .5em; flex: 0 0 auto; }
.cliplist {
  flex: 0 1 auto; min-height: 0;
  overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain;
  display: flex; flex-direction: column; gap: 0;
}
.cliplist::-webkit-scrollbar { width: 0; }
/* Taken out of the centred block and pinned to the foot of the panel. It is a
   footnote about where the headphone is, not part of the list, and riding up
   and down with the number of recordings made it look like one. */
.cliphint {
  position: absolute; left: 3em; right: 2.6em; bottom: 2.4em;
  margin: 0;
}
.noclips {
  padding: 2.4em 2em; text-align: center;
  background: var(--bg-card); border: 1px solid #3a332a; border-radius: var(--radius);
}
.noclips .body { margin-top: .6em; }

/* [OH6] The recording rows lose the box. Three filled rectangles with a hard
   border each were the same default card component the grid was made of, and
   a list of three things does not need three frames to be understood as a
   list. A hairline rule between rows says it.
   The row does NOT stretch to fill the column. That was tried and looked
   worse than the boxes: at 200px with the content centred, each rule sat 50px
   away from the words it belonged to and the list read as an empty table.
   The height it needs is the height it takes, the group is centred, and the
   air around it is the panel's air rather than the row's. The target still
   grows rather than shrinking: 124px against the old 96px, all of it padding,
   so the control is quieter AND easier to hit than the box it replaces. */
.cliprow {
  display: flex; align-items: center; gap: 1.4em; text-align: left;
  flex: 0 0 auto; min-height: 124px;
  padding: 1.15em .5em;
  background: transparent; border: 0; border-radius: 0;
  border-bottom: 1px solid rgba(237,229,216,.12);
  color: var(--ink); font-family: var(--serif); font-size: 1.35em;
  pointer-events: auto;
}
.cliprow:first-child { border-top: 1px solid rgba(237,229,216,.12); }
.cliprow:active { background: rgba(201,162,75,.12); }
.cliprow .clipnum {
  width: 64px; height: 64px; border-radius: 50%; flex: 0 0 auto;
  border: 1.5px solid var(--gold); color: var(--gold);
  display: flex; align-items: center; justify-content: center; font-size: .85em;
}
.cliprow:active .clipnum { background: var(--gold); color: var(--bg); }
/* min-width:0 is what stops a long unbroken clip title from blowing the flex
   row out to 4000px and shoving the CC / AUDIO / COMING SOON badges off-screen.
   [D6] The three-line clamp is gone. A recording title a visitor cannot read
   to the end is a recording they cannot choose; the row grows instead and the
   list scrolls, which it already does. */
.cliprow .cliptitle {
  flex: 1 1 0%; min-width: 0; line-height: 1.3; overflow-wrap: anywhere;
}
.cliprow.unavailable { opacity: .5; }
.cliprow.unavailable:active { background: transparent; }
.cliprow.unavailable:active .clipnum { background: transparent; color: var(--gold); }

/* The chevron closes the row off at the right end. Without it the hairline
   rule ran 500px past the last word of the title and the list read as an
   empty table with one filled column. It is an affordance rather than copy:
   no words, nothing to translate, and it says the row opens something, which
   on a row whose only other mark is a play disc is worth saying. */
.clipgo {
  flex: 0 0 auto; color: var(--gold); opacity: .55;
  font-family: var(--serif); font-size: 1.5em; line-height: 1;
  padding-left: .4em;
}
.cliprow.unavailable .clipgo { opacity: .25; }
.badges { display: flex; gap: .5em; flex: 0 0 auto; }
.badge {
  font-family: var(--sans); font-size: .62em; letter-spacing: .08em;
  border: 1px solid var(--ink-faint); border-radius: 6px;
  padding: .3em .6em; color: var(--ink-dim); white-space: nowrap;
}
.badge.gold { border-color: var(--gold); color: var(--gold); }

/* ---------- player additions ---------- */
.ohstill {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; z-index: 1; filter: brightness(.85);
}
.ohtitle {
  position: absolute; top: 0; left: 0; right: 0; z-index: 61;
  padding: 1.3em 1.6em 2.2em 120px;
  display: flex; flex-direction: column; gap: .25em;
  background: linear-gradient(rgba(0,0,0,.85), transparent);
  transition: opacity .4s; pointer-events: none;
}
.ohtitle.faded { opacity: 0; }
/* The close ✕ must sit above everything the player can put on the glass, so a
   visitor can always leave. Two layers try to cover it: the title bar's dark
   gradient (z-index 61), which used to grey the ✕ out, and the end /
   coming-soon panels (.ohpanel, z-index 62). Sitting at 61 cleared the gradient
   but left the ✕ a DEAD TAP whenever a panel was up: the panel's own full-screen
   scrim swallowed the touch, so the one control a visitor reaches for to escape
   did nothing, on a wall with no member of staff to undo it. At 63 the ✕ is
   always live and closes the player exactly as the panel's own buttons do. */
#pClose { z-index: 63; }
/* [D6] No clamp here either. The title bar fades out four seconds into
   playback anyway, so a long title costs nothing but the height it needs, and
   the check that it never swallows the transport bar is in the suite. */
.ohtitle-main {
  font-family: var(--serif); font-size: 1.7em; color: var(--ink);
  overflow-wrap: anywhere;
}
.ohtitle .caption { overflow-wrap: anywhere; }

#pBar { gap: .8em; }
.ohcc.on { background: var(--gold); color: #000; }
/* [OH8] There is no .ohcc.disabled state any more. A CC button rendered over
   a clip that declares no subtitles is a control that can never do anything,
   and a dead control teaches a visitor the screen is broken. app.js puts
   .hidden on #pCC whenever the open clip declares no subtitles key, so the
   button exists on the bar only when it works. The rule that dimmed it to
   opacity .3 is deliberately gone with the state. */

.ohvol { display: flex; align-items: center; gap: .5em; color: var(--gold); pointer-events: auto; }
.ohvol input[type="range"] {
  -webkit-appearance: none; appearance: none;
  width: 210px; height: var(--touch); background: transparent; pointer-events: auto;
}
.ohvol input[type="range"]::-webkit-slider-runnable-track {
  height: 8px; border-radius: 4px; background: #3a332a;
}
.ohvol input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 40px; height: 40px; margin-top: -16px; border-radius: 50%;
  background: var(--gold); border: none;
}

video::cue {
  background: rgba(0, 0, 0, .72); color: #ede5d8;
  /* Lato carries no Devanagari. No Hindi .vtt has been delivered yet, but the
     day one is, the cues must not come up as empty boxes. */
  font-family: 'Lato', 'Noto Sans Devanagari', sans-serif; font-size: 1.4rem;
}

.ohpanel {
  position: absolute; inset: 0; z-index: 62;
  display: flex; align-items: center; justify-content: center;
  background: rgba(10, 8, 6, .78);
}
.ohpanel-card {
  text-align: center; max-width: 42em; padding: 3em;
  display: flex; flex-direction: column; align-items: center; gap: 1.2em;
  background: var(--bg-raised); border: 1px solid #3a332a; border-radius: var(--radius);
}
.ohpanel-btns { display: flex; gap: 1.2em; flex-wrap: wrap; justify-content: center; }


/* ── escape & abuse hardening (Aug 2026 abuse pass) ───────────────────────────
   Gestures must stay inside the app. `touch-action: manipulation` (kiosk.css)
   still lets Chromium claim a two-finger pinch as a browser zoom: reproduced at
   visualViewport.scale = 4 in testing/test_escape.py, and NOT stopped by the
   --disable-pinch launch flag. Pinning the full-screen layers to
   `touch-action: none` and re-granting only the axis each scroller needs is what
   actually holds. Chrome stops the touch-action walk at the scroller, so
   `none` on the .screen does not disable `pan-y` inside it (measured).
   `overscroll-behavior: none` stops the two-finger overscroll back/forward
   navigation without relying on --overscroll-history-navigation=0. */
html, body { overscroll-behavior: none; }
img, video, svg { -webkit-user-drag: none; user-drag: none; }
#saver, .screen, .player { touch-action: none; }
.grid, .cliplist, .profilecard { touch-action: pan-y; }
input[type=range] { touch-action: none; }   /* the headphone volume slider */

/* ==========================================================================
   GALLERY LEGIBILITY AUDIT (app-local corrections)
   Panel: 32" 1920x1080 landscape, visitors standing 40-80 cm away.
   ========================================================================== */

/* [VIS-1] Subtitles collided with the transport bar. The <video> is
   `flex: 1` and fills the player, so Chrome parks the cue container at the
   very bottom of the video box, which is exactly where .playerbar is
   overlaid. With CC on, the caption line was painted straight across the
   seek bar, between the elapsed and duration readouts. Lift the cue container
   clear of the 96px control strip. Reported upstream: the framework player
   owns both the bar and the <video>, so this belongs in kiosk.css. */
#pVideo::-webkit-media-text-track-container { transform: translateY(-104px); }
video::cue { background: rgba(0, 0, 0, .8); color: var(--ink); }

/* [VIS-2] Gallery reading floor (~18px) and AA body contrast. --ink-faint
   measures 3.48:1 on --bg-raised and 3.84:1 on --bg; the framework .caption
   is .82em = 16.4px here. All app-owned selectors. */
.cliphint,
#pSub { font-size: .95em; color: var(--ink-dim); }
/* [D7] The secondary meta lines (the card's recording count, and the profile's
   craft line) are the smallest thing a visitor reads standing at the 15.6-inch
   panel, so they take the framework's shared 22px floor rather than a bespoke
   .95em (=19px here). The var carries the same value the framework token holds,
   so there is one number, with a literal fallback if that token is ever absent. */
.profmeta,
.cliphead,
.card .cardfoot { font-size: var(--meta-size, max(22px, .9em)); }
.badge { font-size: .75em; }
/* The one line under the app title carries the whole promise of the station
   now, so it is read at body weight rather than as a caption footnote. */
.topbar .title .caption { font-size: 1.05em; color: var(--ink); letter-spacing: 0; }

/* ==========================================================================
   [LANG-2] DEVANAGARI SETTING
   Noto Sans/Serif Devanagari sit taller than EB Garamond and Lato: matras ride
   above the shirorekha and vowel signs hang below the baseline, so any box
   that clips its own overflow needs more leading than Latin does. Everything
   below is scoped to html[lang="hi"], so the English setting is untouched.
   Checked screen by screen at 1920x1080 in Hindi.
   ========================================================================== */

/* Tight display leading clipped the top matras.
   The screensaver is NOT in this list any more: it now paints both languages
   at once whichever way the toggle is set, so its Devanagari leading is set
   unconditionally up in the [D1] block. */
/* 1.35 was measured against the old 2.2em title. At 3.05em the same ratio is
   47px of leading under one line of Devanagari, which opened a hole between
   the title and the line under it. 1.2 still clears the matras at the large
   step, which is the case that decides it. */
html[lang="hi"] .apptitle { line-height: 1.2; }
html[lang="hi"] .profilecard h2 { line-height: 1.35; }
html[lang="hi"] .ohpanel-card h2 { line-height: 1.4; }

/* Boxes whose text can run to a second line: too little leading and the vowel
   signs of one line collide with the matras of the next. */
html[lang="hi"] .card .meta h3 { line-height: 1.45; }
html[lang="hi"] .cliprow .cliptitle { line-height: 1.6; }
html[lang="hi"] .ohtitle-main { line-height: 1.45; }

/* Wide tracking is a Latin small-caps device. In Devanagari it pulls the
   shirorekha apart between clusters and reads as broken type, so the display
   tracking comes off and only a hair is kept for the caption rows.
   The list now includes .cardfoot and the two .eyebrow lines, because those
   are exactly the places the tracking got wider: an eyebrow is set at .14em
   by the framework, which is right for "RECORDINGS" and wrong for
   "रिकॉर्डिंग". uppercase is left alone rather than switched off; it has no
   effect on Devanagari, so saying so would be noise. */
html[lang="hi"] .profmeta { letter-spacing: .02em; }
html[lang="hi"] .badge { letter-spacing: .02em; }
html[lang="hi"] .cliphead { letter-spacing: .02em; }
html[lang="hi"] .card .cardfoot { letter-spacing: .02em; }

/* ==== HH LOGO STYLE, PROPAGATED FROM THE QUIZ (Vivek 16 Aug 21:52) =========
   The quiz's approved logo treatment, applied verbatim (see apps/quiz/app.css
   revisions 11-14 for the full reasoning):
   1. Attract screen: stacked lockup top right - the REAL standalone emblem
      (hh-emblem.png, two-tone, rendered directly, never a single-ink mask)
      over "Handloom Haat" in the credit's voice, upright, small caps.
   2. The framework's horizontal .saver-mark img is hidden; the lockup stands
      in its place.
   3. In-app screens: NO top-right HH mark at all; the framework's bottom-left
      .wm-hh watermark (untouched) is the only in-app HH presence.
   FRAMEWORK CANDIDATE: this block is now duplicated across all six apps and
   belongs in kiosk.css/weave-saver.js on the next framework pass. */
.saver-mark img { display: none; }
.brand-watermarks .wm-hh-top { display: none; }
.saver-hh-row {
  position: absolute; z-index: 4; pointer-events: none;
  right: 1.4em; top: 1.1em;
  display: flex; flex-direction: column; align-items: center; gap: .35em;
}
.saver-hh-sym {
  height: calc(max(18px, .46em) * 2.84);
  width:  calc(max(18px, .46em) * 2.84 * 1.0117);
  background: url(../../framework/assets/hh-emblem.png) center / contain no-repeat;
  filter: drop-shadow(0 1px 4px rgba(8,6,4,.98))
          drop-shadow(0 0 10px rgba(8,6,4,.9))
          drop-shadow(0 0 22px rgba(8,6,4,.7));
}
.saver-hh-word {
  font-family: var(--sans); font-weight: 400; font-style: normal;
  font-size: max(18px, .46em);
  letter-spacing: .06em; text-transform: capitalize;
  font-variant-caps: small-caps; font-variant: small-caps;
  color: var(--ink);
  text-shadow: 0 0 3px rgba(8,6,4,.98), 0 1px 5px rgba(8,6,4,.98),
               0 0 18px rgba(8,6,4,.92), 0 0 34px rgba(8,6,4,.75);
}

/* v2.2.1, 17 Aug, VIVEK'S ITEM 3: "logos OUT of the weave and motif areas
   entirely", read together with his item 4, the khadi border bands. The marks
   have MOVED rather than been deleted: framework/weave-saver.js now weaves a
   khadi border between weave sections carrying one institution's emblem
   repeated saree-border style, cycling per border. So the attract screen's
   top-right lockup is retired here, because the identity it carried is now IN
   the cloth where he asked for it, and leaving both would show the same marks
   twice on one screen.
   HIDDEN, NOT DELETED, and deliberately so: this block is Vivek-approved work
   from 16 Aug 21:52 and the rules below it (.saver-hh-sym/-word geometry) are
   the record of how that lockup was built. One line restores it if he wants it
   back after seeing the borders. Scope: this is the ONLY change made in these
   five directories, per Bob's 11:07 extension.
   NOT TOUCHED: the framework's bottom-left .wm-hh watermark, and the quiz's
   own bottom-right cluster, which is separately Vivek-approved. */
.saver-hh-row { display: none; }
