/* ============================================================
   roomsui.css — styles for the shared CAU.roomsUI module
   (js/roomsui.js: groupByBuilding, BuildingFilter, RoomCard,
   ScheduleView in its four modes).

   LOAD ORDER
   Defines no custom properties of its own: every colour and every size
   below is a var(--...) token from app.css (palette, --space-*, --fs-*,
   --lh-*, --weight-*, --size-*, --radius-*). Load it after app.css and
   components.css:
       <link rel="stylesheet" href="css/app.css">
       <link rel="stylesheet" href="css/components.css">
       <link rel="stylesheet" href="css/roomsui.css">

   NAMESPACE
   Every class is prefixed `ru-` (roomsUI), so nothing here can collide
   with app.css's plain names, components.css's ds- family, or the
   shells' own w- and adm- families.

   MOTION
   Every transition names its properties — a bare `transition:.18s` is
   the pinned theme-switch bug in tests/test_web_theme.py. Hover rules
   live behind @media (hover: hover) and skip the telegram mode, which
   is touch-only by definition.

   And one step further, because naming the property turned out not to
   be enough. Measured in Chromium on this module's own room card: an
   element whose colour comes from a token AND names that colour in a
   transition is left on the previous palette forever when the theme
   changes — `background:var(--surface)` alone follows the switch,
   `background:var(--surface)` plus `transition:background-color` does
   not, and no later repaint rescues it. It is the same bug
   tests/test_web_theme.py pins in its unnamed form; naming the property
   only makes it quieter. So nothing here transitions a colour that
   comes from a token. What is left is transform and opacity, which are
   literals and cannot go stale — the card's press and its photo's lean.
   A hover border simply appears, which is what a hover border did
   before anybody thought to animate one.

   TWO SHAPES, FOUR MODES
   desktop/tablet draw a rooms x time grid; mobile/telegram draw a
   per-room vertical timeline. They are two different components that
   share a palette, not one layout squeezed by a media query — which is
   why the mode is decided in js (ScheduleView's `mode`, or 'auto') and
   the rules below only style whichever one was built.

   DENSITY
   The schedule is drawn in pixels-per-minute (see PX in roomsui.js:
   desktop/tablet 1.6, mobile 1.2, telegram 0.9). The hour/quarter line
   tiles below are sized to those exact numbers — 96/24px for the grid,
   72px for mobile, 54px for telegram — change them together. Room
   column widths are the same kind of pair: COLW in roomsui.js writes the
   per-group minimum inline, and the per-mode minimums below must match
   it (desktop 156, tablet 132, floor 128).

   THE ROOM CARD
   A photograph on top, information underneath, no icon anywhere. The
   frame declares its aspect ratio, so a list of nine cards is already
   its final height before the first picture has loaded; the <img> is
   lazy and object-fit:cover (js/roomsui.js writes both). Six of the
   nine rooms have no photograph today, which makes the placeholder the
   normal state of this card rather than its failure state — it is drawn
   here, in the theme's own ink, and never left as a broken image.

   BREAKPOINTS
   Only the canon widths from app.css's table are used: 375 and 430 for
   the timeline's rail and the card's photo shape, 768 for the point
   where the grid gets its own scroll box and sticky headers and where a
   room card stops being the full width of a phone.
   ============================================================ */

/* app.css already sets *{box-sizing:border-box}, but the fixed header
   heights in the grid depend on it, so it is pinned here too. */
.ru-sched,.ru-sched *,.ru-room-card,.ru-room-card *,.ru-bfilter,.ru-bfilter *{
  box-sizing:border-box;
}
/* The shells load app.css and get [hidden]{display:none} from it; pinned
   here as well so the now-line never leaks on a page that forgot. */
.ru-nowline[hidden],.ru-now-tag[hidden]{display:none}

/* ---------------- BuildingFilter ---------------- */
.ru-bfilter{display:flex;gap:var(--space-7);flex-wrap:wrap}
.ru-bfilter-chip{
  border:1px solid var(--border);background:var(--surface-2);color:var(--text-dim);
  font:var(--weight-620) var(--fs-12-5)/var(--lh-1) var(--font);
  padding:var(--space-9) var(--space-13);border-radius:var(--radius-pill);cursor:pointer;
  min-height:var(--size-34);
  transition:opacity .18s, transform .18s;
}
.ru-bfilter-chip:active{transform:scale(.96)}
.ru-bfilter-chip.ru-on{
  background:var(--accent-grad);border-color:transparent;color:var(--on-accent);
}

/* ---------------- RoomCard ----------------
   A photograph on top, everything else under it. The card is a single column
   now: picture, building, name, subtitle, the meta line (floor / seats /
   working hours), the room's own status, and last the availability strip.

   The two statuses are two things and are drawn as two things:
     .ru-room-state   — the ROOM: active / unavailable / maintenance. Part of
                        the body, told apart by mark shape as well as colour.
     .ru-room-card-status — the SCHEDULE: free or busy right now, in a strip
                        ruled off from the body, and empty (so, hidden) until a
                        host hands one in.

   The frame owns a fixed aspect ratio, so nine cards laying out before a
   single photograph has arrived are already the height they will keep. */
.ru-room-card{
  display:flex;flex-direction:column;align-items:stretch;width:100%;text-align:left;
  background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);
  padding:0;overflow:hidden;cursor:pointer;color:var(--text);
  font:var(--weight-500) var(--fs-13)/var(--lh-1-35) var(--font);box-shadow:var(--shadow);
  transition:transform .16s;
}
.ru-room-card:active{transform:scale(.985);border-color:var(--border-strong)}
/* compact: the telegram host — tighter, no elevation of its own */
.ru-room-card.ru-compact{box-shadow:none;border-radius:var(--radius-11)}

/* -- the photograph -- */
/* 16:10 is the shape the owner's own room pictures come in, and it is the
   whole reason the card cannot jump: the box is this tall before the bytes
   arrive and exactly this tall afterwards. */
.ru-room-photo{
  position:relative;display:block;width:100%;aspect-ratio:16 / 10;overflow:hidden;
  background:var(--surface-2);border-bottom:1px solid var(--border);
}
.ru-room-photo-img{
  display:block;width:100%;height:100%;object-fit:cover;object-position:center;
  transition:transform .3s;
}
/* The placeholder is the common case (six of nine rooms), so it is designed
   rather than defaulted: a quiet outline in the theme's faint ink, a line
   saying why the frame is empty, and the same tinted field the photo sits on. */
.ru-room-photo-none{
  position:absolute;inset:0;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:var(--space-7);
  color:var(--text-faint);padding:var(--space-12);text-align:center;
}
.ru-room-photo-mark{width:var(--size-40);height:auto;opacity:.75;flex:none}
.ru-room-photo-hint{
  font-size:var(--fs-11);font-weight:var(--weight-600);letter-spacing:.3px;
  line-height:var(--lh-1-35);
}
.ru-room-card.ru-compact .ru-room-photo{aspect-ratio:16 / 9}
.ru-room-card.ru-compact .ru-room-photo-mark{width:var(--size-32)}

/* -- the carousel inside that frame --
   The frame above keeps its size, its ratio and its border; everything here
   lives inside it. A card with one photograph looks exactly as it did — the
   dots and the arrows only exist when there is somewhere to go. */
.ru-gal{position:absolute;inset:0;display:block}
.ru-gal-img{display:block;width:100%;height:100%;object-fit:cover;object-position:center}
/* A file that 404s: the frame keeps its shape and shows the tinted field
   rather than the browser's torn-image glyph. */
.ru-gal-broken .ru-gal-img{visibility:hidden}

/* The arrows sit over the picture, so they carry their own scrim: a chevron in
   white on a light photograph is invisible, and one in ink is invisible on a
   dark one. Hidden on touch, where the thirds of the frame are the control and
   a 28px target under a thumb is a miss. */
.ru-gal-nav{
  position:absolute;top:50%;transform:translateY(-50%);
  width:var(--size-28);height:var(--size-28);border-radius:50%;
  display:none;align-items:center;justify-content:center;
  background:var(--photo-nav);color:var(--on-accent);opacity:0;transition:opacity .18s;
}
.ru-gal-nav svg{width:var(--size-16);height:var(--size-16)}
.ru-gal-prev{left:var(--space-8)}
.ru-gal-next{right:var(--space-8)}
@media (hover: hover) and (pointer: fine){
  .ru-gal-many .ru-gal-nav{display:flex}
  .ru-room-card:hover .ru-gal-nav,
  .ru-room-card:focus-visible .ru-gal-nav,
  .ru-gal:hover .ru-gal-nav{opacity:1}
}

.ru-gal-dots{
  position:absolute;left:0;right:0;bottom:var(--space-8);
  display:flex;justify-content:center;gap:var(--space-5);pointer-events:none;
}
.ru-gal-dot{
  width:var(--size-6);height:var(--size-6);border-radius:50%;
  background:var(--photo-dot);box-shadow:0 0 0 1px var(--photo-dot-ring);
  /* Width only. `background` here is a token, and a transition naming a
     colour property sticks on a stale value across a theme switch in
     Chromium — the house rule this module's own test enforces. */
  transition:width .18s;
}
.ru-gal-dot.ru-on{background:var(--on-accent);width:var(--size-14);border-radius:var(--size-6)}

/* -- the photograph, full screen --
   On the body, not in the card: a card sits inside a scrolling column with
   `overflow` on it, and a fixed overlay inside one of those is clipped by it. */
.ru-lb{
  position:fixed;inset:0;z-index:9500;display:flex;align-items:center;justify-content:center;
  background:var(--lightbox-bg);
  padding:calc(var(--space-16) + env(safe-area-inset-top)) var(--space-16)
          calc(var(--space-16) + env(safe-area-inset-bottom));
}
.ru-lb-stage{
  display:flex;align-items:center;justify-content:center;
  width:100%;height:100%;
}
.ru-lb-img{
  max-width:100%;max-height:100%;
  /* `contain`, not `cover`: this is the view somebody opened to see the whole
     room, so cropping it here would defeat the only reason to open it. */
  object-fit:contain;border-radius:var(--radius-8);
}
.ru-lb-close,.ru-lb-nav{
  position:absolute;display:flex;align-items:center;justify-content:center;
  width:var(--size-44);height:var(--size-44);border-radius:50%;
  border:0;background:var(--lightbox-btn);color:var(--on-accent);cursor:pointer;
}
.ru-lb-close:focus-visible,.ru-lb-nav:focus-visible{outline:2px solid var(--on-accent);outline-offset:2px}
@media (hover: hover) and (pointer: fine){
  .ru-lb-close:hover,.ru-lb-nav:hover{background:var(--lightbox-btn-strong)}
}
.ru-lb-close svg,.ru-lb-nav svg{width:var(--size-20);height:var(--size-20)}
.ru-lb-close{top:calc(var(--space-12) + env(safe-area-inset-top));right:var(--space-12)}
.ru-lb-prev{left:var(--space-12);top:50%;transform:translateY(-50%)}
.ru-lb-next{right:var(--space-12);top:50%;transform:translateY(-50%)}
.ru-lb-count{
  position:absolute;left:50%;transform:translateX(-50%);
  bottom:calc(var(--space-14) + env(safe-area-inset-bottom));
  color:var(--on-accent);font-size:var(--fs-13);font-weight:var(--weight-600);
  letter-spacing:.4px;font-variant-numeric:tabular-nums;
}
/* On a phone the side arrows would land under the thumbs that are swiping.
   The gesture is the control there; the count still says where you are. 430 is
   the widest phone in the canon table — a made-up 520 is how a breakpoint
   nobody else knows about gets into the stylesheet. */
@media (max-width:430px){
  .ru-lb-nav{display:none}
}

/* -- the information under it -- */
.ru-room-card-main{
  flex:1;min-width:0;display:flex;flex-direction:column;align-items:flex-start;
  gap:var(--space-3);padding:var(--space-12) var(--space-14);
}
/* Gold on a building name is this module's one constant: the schedule's
   building bands are gold too, so the card and the grid name the same thing
   the same way. */
.ru-room-card-bldg{
  max-width:100%;font-size:var(--fs-11);font-weight:var(--weight-700);
  letter-spacing:.7px;text-transform:uppercase;color:var(--gold);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.ru-room-card-name{
  max-width:100%;font-size:var(--fs-15);font-weight:var(--weight-660);letter-spacing:-.2px;
  line-height:var(--lh-1-25);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.ru-room-card-sub{
  max-width:100%;font-size:var(--fs-12);color:var(--text-dim);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
/* floor · seats · working hours — one wrapping line, dot-separated, and the
   dots are generated so no separator is ever left dangling at a wrap. */
.ru-room-card-meta{
  display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-2) var(--space-8);
  margin-top:var(--space-4);font-size:var(--fs-11-5);color:var(--text-faint);
}
.ru-room-meta-item{white-space:nowrap;display:inline-flex;align-items:center;gap:var(--space-8)}
.ru-room-meta-item + .ru-room-meta-item::before{content:"·";color:var(--border-strong)}

/* -- the room's own status -- */
.ru-room-state{
  display:inline-flex;align-items:center;gap:var(--space-6);margin-top:var(--space-8);
  padding:var(--space-4) var(--space-9);border-radius:var(--radius-pill);
  font:var(--weight-640) var(--fs-11)/var(--lh-1) var(--font);letter-spacing:.2px;
  white-space:nowrap;max-width:100%;overflow:hidden;
}
/* Shape as well as colour: a colour-blind reader, a greyscale printout and a
   projector all still tell the three apart — disc, ring, diamond. */
.ru-state-mark{
  width:var(--size-8);height:var(--size-8);flex:none;display:block;
  border-radius:50%;background:currentColor;
}
.ru-state-active{background:var(--success-soft);color:var(--success)}
.ru-state-maintenance{background:var(--warning-soft);color:var(--warning)}
.ru-state-maintenance .ru-state-mark{
  background:transparent;border:2px solid currentColor;
}
.ru-state-unavailable{background:var(--danger-soft);color:var(--danger)}
.ru-state-unavailable .ru-state-mark{border-radius:var(--radius-2);transform:rotate(45deg)}

/* -- the availability strip: the schedule's answer, not the room's -- */
.ru-room-card-status{
  display:flex;align-items:center;flex-wrap:wrap;gap:var(--space-8);
  padding:var(--space-9) var(--space-14);border-top:1px solid var(--border);
  background:var(--surface-2);
}
.ru-room-card-status:empty{display:none}
.ru-room-card.ru-compact .ru-room-card-main{padding:var(--space-9) var(--space-11)}
.ru-room-card.ru-compact .ru-room-card-status{padding:var(--space-7) var(--space-11)}
.ru-room-card.ru-compact .ru-room-card-name{font-size:var(--fs-13-5)}
.ru-room-card.ru-compact .ru-room-card-sub{display:none}

/* status chip — same visual language as app.css's .chip, own namespace */
.ru-chip{
  display:inline-flex;align-items:center;gap:var(--space-6);
  padding:var(--space-5) var(--space-9);border-radius:var(--radius-pill);
  font:var(--weight-640) var(--fs-11)/var(--lh-1) var(--font);letter-spacing:.2px;white-space:nowrap;
}
.ru-dot{width:var(--size-6);height:var(--size-6);border-radius:50%;display:block;background:currentColor}
.ru-chip-free{background:var(--free-soft);color:var(--free)}
.ru-chip-busy{background:var(--busy-soft);color:var(--busy)}

/* ---------------- ScheduleView: shared ---------------- */
.ru-sched{display:flex;flex-direction:column;gap:var(--space-12)}
.ru-sched-body{display:flex;flex-direction:column;gap:var(--space-12);min-width:0}

/* hour labels — used by the grid axis and the timeline rail alike */
.ru-axis-h{
  position:absolute;right:var(--space-8);transform:translateY(-50%);
  font-size:var(--fs-10);color:var(--text-faint);letter-spacing:.2px;
  font-variant-numeric:tabular-nums;white-space:nowrap;
}

/* the current-time line + its little clock tag on the axis */
.ru-nowline{
  position:absolute;left:0;right:0;height:2px;background:var(--gold);
  z-index:3;pointer-events:none;display:block;
}
.ru-now-tag{
  position:absolute;right:var(--space-4);transform:translateY(-50%);z-index:6;
  padding:var(--space-2) var(--space-5);border-radius:var(--radius-pill);
  background:var(--gold);color:var(--on-accent);
  font:var(--weight-700) var(--fs-9-5)/var(--lh-1) var(--font);font-variant-numeric:tabular-nums;
}

/* ---------------- ScheduleView: grid (desktop / tablet) ---------------- */
.ru-grid-scroll{
  overflow-x:auto;overscroll-behavior-x:contain;
  border:1px solid var(--border);border-radius:var(--r-card);
  background:var(--surface);box-shadow:var(--shadow);
}
.ru-grid{display:flex;min-width:100%}

.ru-grid-axis{
  position:sticky;left:0;z-index:4;flex:none;width:var(--size-52);
  background:var(--surface-solid);border-right:1px solid var(--border);
}
/* Sticky in both directions: the axis column holds the left edge while the
   grid scrolls sideways, and this spacer holds the top-left corner while it
   scrolls down. Opaque, or the hour labels show through the room names. */
.ru-grid-axis-spacer{
  position:sticky;top:0;z-index:5;height:76px;
  background:var(--surface-solid);border-bottom:1px solid var(--border);
}
.ru-grid-axis-body{position:relative}

.ru-grid-group{display:flex;flex-direction:column;min-width:0}
.ru-grid-group + .ru-grid-group{border-left:1px solid var(--border)}
/* The building name is a heading, and gold is what makes a heading read as
   this university's rather than any admin template's. A label, never a fill. */
.ru-grid-bhead{
  position:sticky;top:0;z-index:3;
  display:flex;align-items:center;height:28px;padding:0 var(--space-10);
  background:var(--surface-solid);border-bottom:1px solid var(--border);
  font:var(--weight-700) var(--fs-10)/var(--lh-1) var(--font);letter-spacing:.7px;
  text-transform:uppercase;color:var(--gold);white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis;
}
.ru-grid-rooms{display:flex;flex:1;min-width:0}
.ru-grid-roomcol{flex:1 1 0;min-width:128px;display:flex;flex-direction:column}
.ru-grid-roomcol + .ru-grid-roomcol{border-left:1px solid var(--border)}
/* Sits directly under the building band, so it sticks 28px down. */
.ru-grid-rhead{
  position:sticky;top:28px;z-index:3;
  height:48px;padding:var(--space-6) var(--space-8);
  background:var(--surface-solid);border-bottom:1px solid var(--border);
  display:flex;flex-direction:column;justify-content:center;gap:var(--space-2);min-width:0;
}
.ru-grid-rhead b{
  font-size:var(--fs-12-5);font-weight:var(--weight-640);letter-spacing:-.1px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.ru-grid-rhead span{
  font-size:var(--fs-10-5);color:var(--text-faint);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}

.ru-col{position:relative;min-width:0}
/* 15-minute grid: a line on the hour, a fainter one each quarter.
   Tile heights are 60 and 15 minutes at 1.6 px/min; roomsui.js shifts
   background-position when the opening time is off the hour. */
.ru-sched-desktop .ru-col,
.ru-sched-tablet .ru-col{
  background-image:
    linear-gradient(to bottom, var(--border-strong) 0, var(--border-strong) 1px, transparent 1px),
    linear-gradient(to bottom, var(--border) 0, var(--border) 1px, transparent 1px);
  background-size:100% 96px,100% 24px;
  background-repeat:repeat;
}
.ru-col-empty{display:grid;place-items:center;padding:var(--space-16)}
.ru-col-empty span{
  max-width:120px;text-align:center;line-height:var(--lh-1-45);
  font-size:var(--fs-11-5);color:var(--text-faint);
}

/* occupancy blocks */
.ru-block{
  position:absolute;left:3px;right:3px;z-index:2;overflow:hidden;
  padding:var(--space-3) var(--space-6);border-radius:var(--r-slot);
  border-left:3px solid var(--busy);background:var(--busy-soft);
}
.ru-block b{
  display:block;font:var(--weight-640) var(--fs-10-5)/var(--lh-1-25) var(--font);
  letter-spacing:.1px;color:var(--text);font-variant-numeric:tabular-nums;
}
.ru-block span{
  display:block;margin-top:var(--space-1);font-size:var(--fs-10);color:var(--text-dim);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.ru-block.ru-wait{border-left-color:var(--wait);background:var(--wait-soft)}
.ru-block.ru-mine{border-left-color:var(--accent);background:var(--accent-soft)}
.ru-block.ru-thin{padding:var(--space-1) var(--space-6)}
.ru-block.ru-thin b{font-size:var(--fs-9-5)}
.ru-block.ru-thin span{display:none}

/* A taken slot is a control: tapping it opens the booking's details, which is
   where «Попросить освободить комнату» lives. It looked inert because it was —
   the schedule's click handler returned early on it — so the cursor and the
   press feedback are part of the fix, not decoration. */
.ru-busy{cursor:pointer;-webkit-tap-highlight-color:transparent}
.ru-busy:active{filter:brightness(.96)}
@media (hover: hover) and (pointer: fine){
  .ru-busy:hover{filter:brightness(.97)}
}

/* ---------------- ScheduleView: timeline (mobile / telegram) ---------------- */
.ru-tl-bhead{
  font:var(--weight-700) var(--fs-10)/var(--lh-1) var(--font);letter-spacing:.7px;
  text-transform:uppercase;color:var(--gold);padding:var(--space-4) var(--space-2) 0;
}
.ru-tl-none{
  padding:var(--space-20) var(--space-14);text-align:center;
  border:1px dashed var(--border-strong);border-radius:var(--r-card);
  color:var(--text-faint);font-size:var(--fs-12-5);line-height:var(--lh-1-45);
}
.ru-tl-room{
  background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);
  padding:var(--space-12);box-shadow:var(--shadow);
}
.ru-tl-rhead{
  display:flex;align-items:baseline;gap:var(--space-8);
  margin-bottom:var(--space-10);min-width:0;
}
.ru-tl-rhead b{font-size:var(--fs-14);font-weight:var(--weight-650);letter-spacing:-.2px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ru-tl-rhead span{font-size:var(--fs-11);color:var(--text-faint);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

.ru-tl-body{position:relative;background-repeat:repeat}
/* the hour rail on the left; labels reuse .ru-axis-h */
.ru-tl-rail{
  position:absolute;left:0;top:0;bottom:0;width:44px;
  border-right:1px solid var(--border);
}
/* faint hour line across the day — 60 minutes at each mode's density */
.ru-sched-mobile .ru-tl-body{
  background-image:linear-gradient(to bottom, var(--border) 0, var(--border) 1px, transparent 1px);
  background-size:100% 72px;
}
.ru-sched-telegram .ru-tl-body{
  background-image:linear-gradient(to bottom, var(--border) 0, var(--border) 1px, transparent 1px);
  background-size:100% 54px;
}

/* a free gap is a real button wired to onSlot */
.ru-tl-free{
  position:absolute;left:52px;right:0;z-index:1;text-align:left;cursor:pointer;
  display:flex;flex-direction:column;justify-content:center;gap:var(--space-2);
  padding:var(--space-6) var(--space-10);
  background:transparent;border:1px dashed var(--border-strong);border-radius:var(--r-slot);
  color:var(--text-faint);font:var(--weight-500) var(--fs-11)/var(--lh-1-2) var(--font);
  transition:opacity .18s, transform .18s;
}
.ru-tl-free:active{transform:scale(.99);background-color:var(--free-soft);border-color:var(--free)}
.ru-tl-free b{
  font-size:var(--fs-11-5);font-weight:var(--weight-620);color:var(--text-dim);
  font-variant-numeric:tabular-nums;
}
.ru-tl-free span{font-size:var(--fs-10-5);font-weight:var(--weight-600);color:var(--free)}
.ru-tl-free.ru-thin{flex-direction:row;align-items:center;gap:var(--space-8);
  padding:var(--space-2) var(--space-10)}
.ru-tl-free.ru-thin span{display:none}

/* busy interval cards on the right of the rail */
.ru-tl-card{
  position:absolute;left:52px;right:0;z-index:2;overflow:hidden;
  padding:var(--space-7) var(--space-10);border-radius:var(--r-slot);
  border-left:3px solid var(--busy);background:var(--busy-soft);
  font:var(--weight-500) var(--fs-11)/var(--lh-1-35) var(--font);color:var(--text-dim);
}
.ru-tl-card b{
  font-size:var(--fs-12);font-weight:var(--weight-650);color:var(--text);
  font-variant-numeric:tabular-nums;letter-spacing:.1px;
}
.ru-tl-owner{display:block;margin-top:var(--space-1);font-size:var(--fs-11);color:var(--text-dim);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ru-tl-ppl{display:inline-block;margin-top:var(--space-2);font-size:var(--fs-10-5);color:var(--text-faint)}
.ru-tl-note{display:block;margin-top:var(--space-2);font-size:var(--fs-10-5);font-style:normal;
  color:var(--text-faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ru-tl-badge{
  display:inline-block;margin-left:var(--space-6);vertical-align:1px;
  padding:var(--space-1) var(--space-6);border-radius:var(--radius-pill);
  background:var(--wait-soft);color:var(--wait);font-style:normal;
  font:var(--weight-700) var(--fs-9-5)/var(--lh-1-35) var(--font);letter-spacing:.3px;text-transform:uppercase;
}
.ru-tl-card.ru-wait{border-left-color:var(--wait);background:var(--wait-soft)}
.ru-tl-card.ru-mine{border-left-color:var(--accent);background:var(--accent-soft)}
.ru-tl-card.ru-thin{padding:var(--space-2) var(--space-10)}
.ru-tl-card.ru-thin .ru-tl-ppl,
.ru-tl-card.ru-thin .ru-tl-note{display:none}
.ru-tl-card.ru-thin .ru-tl-owner{display:inline;margin-left:var(--space-6);margin-top:0}

/* telegram: same timeline, tighter everything; hover never applies (below) */
.ru-sched-telegram .ru-sched-body{gap:var(--space-8)}
.ru-sched-telegram .ru-tl-room{padding:var(--space-8);border-radius:var(--radius-11);box-shadow:none}
.ru-sched-telegram .ru-tl-rhead{margin-bottom:var(--space-7)}
.ru-sched-telegram .ru-tl-rail{width:40px}
.ru-sched-telegram .ru-tl-free,
.ru-sched-telegram .ru-tl-card{left:46px;padding:var(--space-4) var(--space-8)}
.ru-sched-telegram .ru-tl-bhead{padding-top:var(--space-2)}

/* ---------------- responsive ---------------- */
/* The smallest phone in the canon. 44px of rail is a quarter of a 375px
   screen; the hour labels still fit at 36 because they are four tabular
   digits, and everything to the right of them gains the difference. */
@media (max-width: 375px){
  .ru-sched-mobile .ru-tl-rail{width:36px}
  .ru-sched-mobile .ru-tl-free,
  .ru-sched-mobile .ru-tl-card{left:42px}
  .ru-tl-room{padding:var(--space-10)}
  .ru-room-card-main{padding:var(--space-10) var(--space-11)}
  .ru-room-card-status{padding:var(--space-8) var(--space-11)}
}
/* Phones generally: a 16:10 frame across a single-column list is a lot of
   screen for a picture, so it shortens — the card keeps a fixed ratio, just a
   flatter one, and still never jumps. */
@media (max-width: 430px){
  .ru-room-photo{aspect-ratio:16 / 9}
}
/* Two or three cards abreast: the picture is smaller on the page, so it can
   afford its full shape again. */
@media (min-width: 768px){
  .ru-room-photo{aspect-ratio:3 / 2}
}
/* Tablet and up: the grid gets a scroll box of its own, so a nine-hour day
   does not push the page down past the room names. The heads are already
   sticky; this is the ancestor that makes them mean something. */
@media (min-width: 768px){
  .ru-sched-desktop .ru-grid-scroll,
  .ru-sched-tablet .ru-grid-scroll{overflow-y:auto;max-height:78vh;overscroll-behavior-y:contain}
  .ru-sched-tablet .ru-grid-roomcol{min-width:132px}
}
/* Laptop: wider room columns — a column narrower than about 150px cannot
   hold "10:30–12:00" and a person's name on two lines, which is the whole
   point of the grid. Matches COLW.desktop in roomsui.js. */
@media (min-width: 1024px){
  .ru-sched-desktop .ru-grid-roomcol{min-width:156px}
  .ru-sched-desktop .ru-grid-axis{width:var(--size-56)}
}

/* ---------------- hover (mouse only; telegram is touch-only) ---------------- */
@media (hover: hover){
  .ru-room-card:hover{border-color:var(--border-strong);box-shadow:var(--shadow-2)}
  /* The photograph leans in a little — the only motion on the card, and the
     one that says the whole thing is a button. */
  .ru-room-card:hover .ru-room-photo-img{transform:scale(1.04)}
  .ru-bfilter-chip:hover{border-color:var(--border-strong);color:var(--text)}
  .ru-bfilter-chip.ru-on:hover{border-color:transparent;color:var(--on-accent)}
  .ru-sched:not(.ru-sched-telegram) .ru-tl-free:hover{
    background-color:var(--free-soft);border-color:var(--free);color:var(--text-dim);
  }
  .ru-sched-desktop .ru-col:hover,
  .ru-sched-tablet .ru-col:hover{background-color:var(--hover)}
  .ru-col-empty:hover{background-color:transparent}
}

/* ---------------- reduced motion ----------------
   app.css already collapses every transition-duration to nothing for somebody
   who has asked their system for less motion, but a collapsed duration still
   *arrives* at the scaled state instantly. The one transform on this card is
   decoration, so it is switched off rather than sped up. */
@media (prefers-reduced-motion: reduce){
  .ru-room-photo-img{transform:none}
}

/* -- the day, as a strip --------------------------------------------------
   Four states and no legend: free, busy, waiting, mine. It sits between the
   body and the availability chip because that is the order the eye wants —
   which room, how full, and only then the word.

   The palette is the one the room page's own timeline uses, so a card and the
   page it opens describe the same day in the same colours. The band draws
   nothing when the host passes no cells, which is what a shell that has not
   loaded the day yet passes. */
/* The height belongs to the cells, not to the band. `box-sizing: border-box`
   is global here, so a 6px band with 8px of padding above it collapsed the
   cells to nothing — the strip was in the DOM, the right width, and invisible. */
.ru-room-load{
  display:flex;gap:var(--space-1-5);align-items:stretch;
  padding:var(--space-8) var(--space-14) 0;
}
/* No transition on the fill. This file's own rule — a colour that comes from a
   token must not be animated, because a token flips with the theme and the flip
   then plays as a fade across every cell at once. */
.ru-load-cell{flex:1;height:var(--size-6);border-radius:var(--radius-2);background:var(--tl-empty)}
.ru-load-cell.ru-load-b{background:var(--busy)}
.ru-load-cell.ru-load-w{background:var(--wait)}
.ru-load-cell.ru-load-m{background:var(--accent)}
.ru-room-load:empty{display:none}
.ru-room-card.ru-compact .ru-room-load{padding:var(--space-7) var(--space-11) 0}

/* The room's own condition, on its card. Same shape as the state chip above so
   the two stack without arguing, and the campus gold rather than a status
   colour: it is not one of the seven things that can happen to a booking. */
.ru-room-cond{
  display:inline-flex;align-items:center;margin-top:var(--space-8);
  padding:var(--space-4) var(--space-9);border-radius:var(--radius-pill);
  border:1px solid var(--gold-line);background:var(--gold-soft);color:var(--gold);
  font:var(--weight-640) var(--fs-11)/var(--lh-1) var(--font);letter-spacing:.2px;
  white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis
}
