/* ===========================================================================
   Loopreel — one stylesheet for the whole site
   =========================================================================== */

:root{
  --bg:#111111;
  --card-bg:#1a1a1a;
  --line:#2a2a2a;
  --grey:#c8c5b8;
  --accent:#26d036;
  --teal:#2ad4c4;
  --muted:#9a9a9a;
  /* Card titles. Soft yellow, ~15:1 against the page background. */
  --title:#f0e2a4;

  /* A surface that sits below the page rather than on it: inputs, the inside
     of a pill, the foot of a gradient. */
  --sunken:#141414;

  /* The sticky header and the drawer, both of which sit over moving content
     and so are the page colour with some transparency. */
  --header-bg:rgba(17,17,17,.94);
  --nav-bg:rgba(17,17,17,.88);

  /* Text that steps forward on hover. */
  --text-strong:#ffffff;

  --radius:10px;

  /* Tells the browser which way round this page is, so its own parts — the
     canvas between pages, scrollbars, form controls, the native video
     controls — match instead of flashing white. */
  color-scheme:dark;
}

/**
 * The light theme.
 *
 * Every colour was measured against its background rather than picked by eye:
 * body text 15.5:1, muted text 5.6:1, card titles 6.3:1, green 5.5:1, teal
 * 5.0:1 — all above the 4.5:1 that small text needs to stay readable.
 *
 * Black stays black where it sits over video: the letterbox behind a player,
 * the shadow under a duration badge. Those are not page colours.
 */
:root[data-theme="light"]{
  color-scheme:light;

  --bg:#ffffff;
  --card-bg:#f6f6f4;
  --sunken:#eceae6;
  --line:#ddd9d2;
  --grey:#26241f;
  --muted:#6a6862;
  --accent:#0b7a16;
  /* Darker than it looks like it needs to be on white, because teal is also
     used on the sunken surfaces — tag pills, inputs — where the background
     is closer to it. 5.0:1 there, 6.0:1 on white. */
  --teal:#0b6f66;
  --title:#7a5c00;
  --header-bg:rgba(255,255,255,.94);
  --nav-bg:rgba(255,255,255,.92);
  --text-strong:#000000;
}

/* The canvas itself, not just the body box. Without this the browser paints
   its own default underneath during a navigation, and that is the flash. */
html{background:var(--bg);}
*{box-sizing:border-box;}
html,body{margin:0;padding:0;}
body{
  background:var(--bg);
  color:var(--grey);
  font-family:Arial,sans-serif;
  font-size:9pt;
  line-height:1.5;
}
h1,h2,h3{
  font-family:Arial,sans-serif;
  background:-webkit-linear-gradient(var(--grey), var(--teal));
  -webkit-background-clip:text;-webkit-text-fill-color:transparent;
  padding:8px 3px;margin:0;line-height:26px;
}
h1{font-size:130%;}
h2{font-size:120%;}
h3{font-size:110%;}
a{color:var(--accent);text-decoration:none;}
a:visited{color:var(--accent);}
a:hover{color:var(--grey);}
img,svg{display:block;}

/* No width cap. The 1280px one left ~160px of dead space down each side of a
   1600px screen — the player broke out of it and everything below did not,
   which is what read as a gap on the right. The small padding is all that keeps
   cards off the very edge. Blocks that genuinely need a reading width set their
   own (see .sm-intro, .contact-form). */
.wrap{margin:0 auto;padding:0 8px;}
.empty{color:var(--muted);padding:20px 4px;}

/* ---------------------------------------------------------------- shell --- */
.shell{min-height:100vh;}

.sidebar{
  width:220px;position:fixed;left:0;top:0;bottom:0;z-index:30;
  overflow-y:auto;border-right:1px solid var(--line);padding:14px 12px;
  display:flex;flex-direction:column;gap:18px;
  background:var(--bg);scrollbar-width:thin;
  transform:translateX(-100%);transition:transform .2s ease;
}
.sidebar.open{transform:translateX(0);}

.brand{
  display:flex;align-items:center;gap:6px;flex-shrink:0;
  font-weight:bold;font-size:13pt;
  background:-webkit-linear-gradient(var(--grey), var(--teal));
  -webkit-background-clip:text;-webkit-text-fill-color:transparent;
}
.brand .icon{font-size:16pt;-webkit-text-fill-color:initial;}

nav.side-nav{display:flex;flex-direction:column;gap:2px;}
nav.side-nav a{
  display:flex;align-items:center;gap:10px;
  padding:8px 10px;border-radius:5px;
  font-size:10.5pt;font-weight:bold;color:var(--accent);
}
nav.side-nav a:visited{color:var(--accent);}
nav.side-nav a .icon{font-size:12pt;width:18px;text-align:center;}
nav.side-nav a:hover{background:var(--card-bg);color:var(--grey);}
nav.side-nav a.active{background:var(--card-bg);color:var(--teal);}

.main{width:100%;container-type:inline-size;}

header{
  background:var(--header-bg);
  backdrop-filter:blur(6px);
  border-bottom:1px solid var(--line);
  position:sticky;top:0;z-index:20;
}
.header-row{
  display:grid;grid-template-columns:36px 1fr 36px;align-items:center;
  padding:10px 0;
}
.menu-toggle{
  grid-column:1;justify-self:start;
  background:none;border:0;color:var(--grey);font-size:16pt;cursor:pointer;padding:0 4px;
}
.home-btn{
  grid-column:2;justify-self:center;display:flex;align-items:center;gap:6px;
  padding:6px 18px;border-radius:20px;
  background:linear-gradient(var(--card-bg), var(--sunken));
  color:var(--accent);font-weight:bold;font-size:10.5pt;
}
.home-btn:hover{color:var(--teal);}

.scrim{display:none;position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:29;}
.scrim.open{display:block;}

/* ------------------------------------------------------- sort pill row --- */
.cat-bar{display:flex;gap:6px;overflow-x:auto;padding:10px 0 4px;scrollbar-width:none;}
.cat-bar::-webkit-scrollbar{display:none;}
.cat-bar a{
  flex-shrink:0;padding:0 12px;height:24px;line-height:24px;border-radius:3px;
  background:linear-gradient(var(--card-bg), var(--sunken));
  font-size:9pt;font-weight:bold;color:var(--accent);
}
.cat-bar a:hover{color:var(--grey);}
.cat-bar a.active{color:var(--teal);}

/* ------------------------------------- video grid: 1 / 2 / 3 / 4 columns --- */
.content{display:flex;flex-wrap:wrap;justify-content:flex-start;gap:0;padding-top:6px;}
.th{
  position:relative;width:calc(100% - 8px);min-width:96px;max-width:500px;
  margin:4px 4px 10px 4px;
}
@container (min-width:520px){.th{width:calc(50% - 8px);margin-bottom:16px;}}
@container (min-width:760px){.th{width:calc(33.33% - 8px);}}
@container (min-width:980px){.th{width:calc(25% - 8px);}}
/* Past four columns a card's own max-width:500px starts to bind, and flex
   leaves the slack at the right end of every row — the same gap in a new place.
   More columns keep each card under that cap so the rows stay flush. */
@container (min-width:1280px){.th{width:calc(20% - 8px);}}
@container (min-width:1600px){.th{width:calc(16.666% - 8px);}}
@container (min-width:1920px){.th{width:calc(14.285% - 8px);}}

.thumb{
  position:relative;border-radius:var(--radius);overflow:hidden;
  aspect-ratio:16/9;background:#000;
}
.thumb .art{
  position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  font-size:30px;transition:transform .3s ease;
}
/* real generated/uploaded thumbnails */
.thumb img.art{width:100%;height:100%;object-fit:cover;}
.th:hover .art{transform:scale(1.05);}
.thumb .duration{
  position:absolute;bottom:4px;right:4px;
  font-size:9pt;font-weight:bold;color:#fff;
  text-shadow:-1px -1px 3px #000,1px -1px 3px #000,-1px 1px 3px #000,1px 1px 3px #000;
  padding:0 3px;pointer-events:none;user-select:none;
}
.thumb .live,.player .live{
  position:absolute;top:6px;left:6px;font-size:8pt;font-weight:bold;
  color:#111;background:var(--teal);padding:2px 6px;border-radius:4px;
  text-transform:uppercase;letter-spacing:.03em;
}
/* One line, cut with an ellipsis.

   Two lines meant a grid where some cards were a line taller than their
   neighbours, and the row of view counts underneath stepped up and down
   with them. One line is the same height for every card, whatever the
   title, so the meta row lands in a straight line across the grid.

   The full title is still on the card as a tooltip, and the play page has
   it in full. */
.desc{
  font-size:10pt;font-weight:bold;text-align:left;color:var(--title);
  line-height:20px;height:20px;margin-top:6px;
  overflow:hidden;white-space:nowrap;text-overflow:ellipsis;
}

/* Views on the left, like share on the right, under the title. */
.card-meta{
  display:flex;justify-content:space-between;align-items:center;gap:8px;
  margin-top:3px;font-size:9pt;color:var(--muted);
}
.card-meta .views,.card-meta .likes{
  display:inline-flex;align-items:center;gap:4px;white-space:nowrap;
}
.card-meta .likes{color:var(--teal);}
/* An unvoted video shows the same 100% as any other, so a fresh upload does
   not stand out as unrated. The .unvoted class stays on the element for the
   tooltip and as a styling hook: add
       .card-meta .likes.unvoted{color:var(--muted);}
   to dim them instead. */
.th:hover .desc{color:var(--text-strong);}
.th:hover .card-meta .views{color:var(--grey);}

/* ------------------------------------------------------------ pagination --- */
.pages{display:flex;justify-content:center;gap:6px;margin:20px 0 12px;flex-wrap:wrap;}
.pages a,.pages span{
  min-width:30px;height:30px;padding:0 4px;border-radius:5px;
  display:flex;align-items:center;justify-content:center;
  font-size:11pt;font-weight:bold;
  background:linear-gradient(var(--card-bg), var(--sunken));color:var(--accent);
}
.pages span.current{color:var(--teal);}
.pages span.gap{background:none;color:var(--muted);min-width:16px;}
.pages a:hover{color:var(--grey);}

/* ================================================== play page ============ */
.watch{padding-top:8px;}
.watch-main{width:calc(100% - 8px);margin:0 4px;}
/* The player runs edge to edge.
 *
 * Everything else sits inside .wrap; these negative inline margins pull this one
 * element back out to the viewport edges. --sbw is the scrollbar width, measured
 * in app.js — viewport units count the scrollbar as part of the viewport while
 * the layout does not, so without it the breakout overshoots and the page gains
 * a horizontal scrollbar.
 *
 * There is deliberately no max-height. Capping it looked sensible, but a browser
 * viewport is wider than 16:9 once the toolbar and taskbar have taken their
 * share — 1872x825 is a ratio of 2.27 — so limiting the player to the viewport
 * height also limited its width, leaving a 405px strip down the right. Width
 * wins: the player fills the screen and its bottom edge falls just below the
 * fold, which is a short scroll rather than a permanent gap.
 */
.player-box{
  margin-inline:calc(50% - 50svw + var(--sbw, 0px) / 2);
  max-width:none;
  aspect-ratio:16/9;
  border-radius:0;
}
/* real uploaded video */
.player-video{
  display:block;width:100%;aspect-ratio:16/9;background:#000;
  border-radius:var(--radius);
}

.tags{display:flex;flex-wrap:wrap;gap:7px;margin:12px 3px 0;}

/* Each tag is its own place to go, so it is shaped like something you
   press rather than like a word with a hash in front of it. */
.tags a{
  display:inline-flex;align-items:center;
  padding:5px 13px;border-radius:999px;
  background:var(--sunken);border:1px solid var(--line);
  font-size:9pt;font-weight:bold;color:var(--teal);
  transition:border-color .15s ease,color .15s ease,background .15s ease;
}
.tags a .hash{color:var(--muted);margin-right:3px;font-weight:normal;}
.tags a:hover{
  background:var(--card-bg);border-color:var(--teal);color:var(--text-strong);
}
.tags a:hover .hash{color:var(--teal);}
.tags a:focus-visible{outline:2px solid var(--teal);outline-offset:2px;}

.player{
  position:relative;border-radius:var(--radius);overflow:hidden;
  aspect-ratio:16/9;background:#000;
}
.player .art{
  position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  font-size:64px;
}
.player .shade{position:absolute;inset:0;background:linear-gradient(rgba(0,0,0,.10),rgba(0,0,0,.55));}
.play-toggle{
  position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  width:66px;height:66px;border-radius:50%;border:0;cursor:pointer;
  background:var(--nav-bg);color:var(--teal);
  font-size:20pt;line-height:1;
  display:flex;align-items:center;justify-content:center;
  transition:transform .2s ease,color .2s ease,opacity .2s ease;
}
.play-toggle:hover{color:var(--grey);transform:translate(-50%,-50%) scale(1.06);}
.controls{
  position:absolute;left:0;right:0;bottom:0;padding:26px 10px 9px;
  display:flex;align-items:center;gap:10px;
  /* A fade, not a band. The old version stepped to 82% black over 41px,
     which on a full-width player looked like a black stripe under the
     video. This reaches the same darkness only at the very bottom, where
     the buttons actually are. */
  background:linear-gradient(to top, rgba(0,0,0,.70) 0%, rgba(0,0,0,.34) 45%, rgba(0,0,0,0) 100%);
}
.controls button{
  background:none;border:0;color:#fff;cursor:pointer;
  font-size:11pt;line-height:1;padding:0;
}
.controls button:hover{color:var(--teal);}
.seek{
  flex:1;height:4px;border-radius:2px;background:rgba(255,255,255,.25);
  position:relative;cursor:pointer;
}
.seek .played{position:absolute;left:0;top:0;bottom:0;width:0;background:var(--accent);border-radius:2px;}
.seek .knob{
  position:absolute;top:50%;left:0;transform:translate(-50%,-50%);
  width:11px;height:11px;border-radius:50%;background:var(--accent);
}
.time{font-size:9pt;font-weight:bold;color:#fff;white-space:nowrap;text-shadow:0 1px 2px #000;}

.vid-meta{
  display:flex;flex-wrap:wrap;gap:8px;align-items:center;justify-content:space-between;
  margin:2px 3px 12px;font-size:9pt;color:var(--muted);
}
.actions{display:flex;flex-wrap:wrap;gap:6px;}
.btn{
  display:flex;align-items:center;gap:5px;
  padding:0 12px;height:26px;border-radius:3px;border:0;cursor:pointer;
  background:linear-gradient(var(--card-bg), var(--sunken));
  color:var(--accent);font-weight:bold;font-size:9pt;font-family:inherit;
}
.btn:hover{color:var(--grey);}
.btn.on{color:var(--teal);}
.btn[disabled]{opacity:.55;cursor:default;}
.btn.vote .count{font-variant-numeric:tabular-nums;}
/* Available, not advertised: quieter than Like and Share beside it. */
.btn.report{color:var(--muted);text-decoration:none;}
.btn.report:hover{color:#ff9b7a;}

/* On a phone the block between the player and Related Videos is three stacked
   rows — title, meta, buttons — and the default spacing made it sprawl. Tighten
   the rhythm below two columns; the desktop layout is unchanged. */
@container (max-width:519px){
  .watch h1{padding-top:2px;padding-bottom:4px;line-height:22px;}
  .vid-meta{gap:6px;margin-bottom:8px;}
  .actions{gap:5px;}
  .btn{padding:0 10px;}
  .sec-head{margin-top:8px;}
  .tags{margin-top:6px;}
}

/* ------------------------------------------------ related section header --- */
/* Three tracks with the heading in the middle one: the empty first track
   mirrors the Autoplay control, so "Related Videos" sits at the true centre of
   the row rather than in the middle of whatever space is left over. */
.sec-head{
  display:grid;grid-template-columns:1fr auto 1fr;align-items:center;
  gap:8px;margin:14px 3px 0;
}
.sec-head h2{grid-column:2;text-align:center;}
.sec-head .autoplay{grid-column:3;justify-self:end;}
.autoplay{display:flex;align-items:center;gap:6px;font-size:9pt;color:var(--muted);}
.switch{
  width:34px;height:18px;border-radius:10px;cursor:pointer;padding:0;
  background:var(--sunken);border:1px solid var(--line);position:relative;
}
.switch i{
  position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:50%;
  background:var(--muted);transition:left .2s ease,background .2s ease;
}
.switch.on{background:rgba(42,212,196,.18);border-color:var(--teal);}
.switch.on i{left:18px;background:var(--teal);}

/* --------------------------------------------------- contact / report ----- */
.note-card{
  border:1px solid var(--line);border-radius:var(--radius);background:var(--card-bg);
  padding:12px 14px;margin:8px 3px;max-width:70ch;font-size:9.5pt;
}
.note-card p{margin:0 0 6px;}
.note-card p:last-child{margin-bottom:0;}
.note-card.ok{border-color:rgba(38,208,54,.45);}
.note-card.warn{border-color:rgba(255,184,74,.5);color:#ffb84a;}
.muted-label{color:var(--muted);font-size:9pt;}

.contact-form{
  display:flex;flex-direction:column;gap:14px;
  max-width:70ch;margin:10px 3px 0;
}
.c-row{display:flex;gap:14px;flex-wrap:wrap;}
.c-row .c-field{flex:1;min-width:200px;}
.c-field{display:flex;flex-direction:column;gap:5px;}
.c-label{font-size:9.5pt;font-weight:bold;color:var(--grey);}
.req{color:#ff9b7a;}
.contact-form input[type=text],
.contact-form input[type=email],
.contact-form select,
.contact-form textarea{
  background:var(--sunken);border:1px solid var(--line);border-radius:5px;
  color:var(--grey);padding:8px 10px;font-family:inherit;font-size:10pt;width:100%;
}
.contact-form textarea{resize:vertical;min-height:110px;}
.contact-form input:focus,
.contact-form select:focus,
.contact-form textarea:focus{outline:none;border-color:var(--teal);}
.contact-form small{color:var(--muted);font-size:9pt;}

.c-actions{display:flex;align-items:center;gap:14px;}
.primary-btn{
  background:linear-gradient(var(--card-bg), var(--sunken));
  border:1px solid var(--accent);color:var(--accent);
  height:32px;padding:0 20px;
}
.primary-btn:hover{color:var(--grey);border-color:var(--grey);}
.c-cancel{color:var(--muted);font-size:9.5pt;}

/* The honeypot. Kept out of view without display:none, which some bots check. */
.trap{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden;}

/* ------------------------------------------------------------- ad boxes --- */
/* Only ever wraps a placement that actually has code — an empty slot prints
   nothing at all, so these rules never leave a gap behind. */
.ad-box{
  display:flex;justify-content:center;align-items:center;
  margin:10px 3px;max-width:100%;overflow:hidden;
}
/* Ad units carry their own fixed sizes; stop an oversized one from widening the
   page and forcing a sideways scroll. */
.ad-box > *{max-width:100%;}
.ad-box img,.ad-box iframe{max-width:100%;height:auto;}

/* Inside the thumbnail grid. flex-basis:100% makes it claim a whole row, so
   the columns either side of it stay aligned instead of reflowing around a
   half-width block. */
.ad-in-content{width:100%;flex-basis:100%;margin:6px 3px 14px;}

footer{
  border-top:1px solid var(--line);margin-top:20px;padding:24px 8px 40px;
  color:var(--muted);font-size:9pt;text-align:center;
}
footer a{color:var(--muted);}
footer a:hover{color:var(--grey);}

/* --------------------------------------------------------- sitemap page --- */
.sm-intro{max-width:78ch;margin:0 3px 6px;color:var(--muted);font-size:9.5pt;}
.sm-intro b{color:var(--grey);}

.sm-browse{margin:0 3px;}
.sm-links{list-style:none;display:flex;flex-wrap:wrap;gap:6px;margin:4px 0 8px;padding:0;}
.sm-links a{
  display:inline-block;padding:4px 10px;border:1px solid var(--line);
  border-radius:var(--radius);background:var(--card-bg);
  color:var(--muted);font-size:9pt;white-space:nowrap;
}
.sm-links a:hover{color:var(--teal);border-color:var(--teal);}

.sm-sec{
  border:1px solid var(--line);border-radius:var(--radius);
  background:var(--card-bg);margin:10px 3px;padding:4px 12px 12px;
}
.sm-sec h2{display:flex;align-items:center;gap:8px;}
.sm-count{
  -webkit-text-fill-color:initial;
  font-size:8.5pt;font-weight:normal;color:var(--muted);
  border:1px solid var(--line);border-radius:10px;padding:0 8px;line-height:16px;
}

.sm-list{list-style:none;margin:0;padding:0;columns:1;}
@media (min-width:620px){.sm-list{columns:2;column-gap:24px;}}
@media (min-width:1000px){.sm-list{columns:3;column-gap:24px;}}
.sm-list li{
  break-inside:avoid;padding:3px 0;border-bottom:1px solid var(--line);
  display:flex;justify-content:space-between;align-items:baseline;gap:10px;
}
.sm-list li:last-child{border-bottom:0;}
.sm-list li a{
  font-size:9.5pt;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;
}
.sm-meta{color:var(--muted);font-size:8.5pt;white-space:nowrap;flex-shrink:0;}

/* An uploaded logo replaces the emoji and the site name in the brand. Height is
   what is constrained, so any aspect ratio lands the same size in the bar. */
.brand-logo{max-height:34px;max-width:100%;width:auto;display:block;}

/* --------------------------------------------------- card, when pressed ---
   The whole card is one link, so the browser's own tap highlight paints a grey
   rectangle over the thumbnail, the title and the meta line at once. Over the
   picture nobody notices it; next to the title, on flat background, it reads as
   a flash. The same click can also start a text selection on the title if the
   pointer moves a hair, which flashes in a second way.

   Both are replaced rather than simply removed: the thumbnail eases back a
   little under the press, which says the same thing without a rectangle. */
.th{-webkit-tap-highlight-color:transparent;}
.th .desc,.th .card-meta{user-select:none;-webkit-user-select:none;}
.th:active .art{transform:scale(.99);}

/* Keyboard focus has to stay visible — this replaces the browser's ring with
   one that matches the theme, it does not take it away. */
.th:focus-visible{
  outline:2px solid var(--teal);outline-offset:3px;border-radius:var(--radius);
}

/* ------------------------------------------------------ in.php player --- */
/* The showcase above the grid. The player box itself is the watch page's, so
   it already knows how to hold a 16:9 video; this is only the spacing and the
   title under it. */
.lander-player{margin:0 auto 18px;max-width:720px;}
.lander-player .player-box{margin-inline:0;border-radius:var(--radius);overflow:hidden;}
/* The meta line and tags sit under the player, inside its width. */
.lander-player .vid-meta{margin-bottom:0;}
.lander-player .tags{margin-top:10px;}
.lander-title{
  font-size:13pt;
  margin:10px 0 0;
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
}
