/* ===================================================================
   THE FEED CARD — post and comment.

   Extracted from feed.php's page-local <style> on 2026-07-28, at the end
   of Tier 3. The markup for these cards became a shared server-side
   component earlier in that tier (includes/feed-post-card.php and
   includes/feed-comment-card.php); this file is the other half of the
   same move, and without it that component is not actually shared.

   The lesson, learned by measuring rather than reasoning: My Signal
   rendered the shared card markup and produced 1440px avatars against a
   44px intent, overflowing its viewport by 482px, purely because 24 of
   the card's 28 classes lived only inside feed.php. A shared component
   whose stylesheet is page-local is not shared. It renders correctly on
   exactly one page and nowhere else, and nothing about the markup
   extraction reveals that until you render it somewhere new.

   LOAD ORDER IS LOAD-BEARING. This file must be linked AFTER site.css and
   BEFORE the including page's own <style> block. That is the position
   these rules already occupied inside feed.php, so keeping it preserves
   the cascade exactly: every rule that used to come before them still
   does, and every rule that used to come after them still does. Move the
   link and you silently change which rule wins.

   Loaded by: feed.php, my-signal.php.

   NOT included here, deliberately: the reaction component
   (.reaction-tray, .reaction-pill, .react-control and friends) already
   lives in site.css, which is why reactions were the only part of the
   card that worked when it was first rendered off-Feed.
   =================================================================== */

.avatar-img { object-fit: cover; }
.composer-avatar, .post-avatar, .comment-avatar { border-radius: 50%; display: flex; align-items: center; justify-content: center; font-family: var(--display); font-weight: 600; color: #03060d; flex-shrink: 0; }
.composer-avatar, .post-avatar { width: 36px; height: 36px; font-size: .88rem; }
.comment-avatar { width: 26px; height: 26px; font-size: .72rem; }
.avatar-link { text-decoration: none; display: block; flex-shrink: 0; }
.comment-gif-btn { width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; border-radius: 5px; background: none; border: none; padding: 0; cursor: pointer; color: var(--text-dimmer); transition: color .2s; }
.comment-gif-btn:hover { color: var(--star); background: rgba(var(--ion-rgb),.12); }
.comment-gif-btn svg { width: 22px; height: 16px; }
.composer-preview.gif-staged img, .comment-preview-wrap.gif-staged img { max-height: 120px; border-radius: 8px; }
.axiom-gif-mount { display: block; }
/* ============================================================
   AMBIENT GLOW -- driven by the VIEWER's active skin.
   ------------------------------------------------------------
   --ion-rgb is already a comma-separated triple in every skin
   palette (site.css :root for Default; axiom_skin_style_tag()
   overrides it at :root for Nebula/Ember/Aurora), which is the
   same shape My Signal's --sig-accent-rgb uses. So this needs no
   new variable, no mapping table and no per-skin CSS -- a fifth
   skin inherits it automatically just by defining --ion-rgb.

   Deliberately quieter than My Signal (.28/.10 there, .13/.05
   here) and deliberately not animated. My Signal's gold says who
   you are and sits on ~12 cards; this says where you are and sits
   on an infinite scroll, where dozens of animated shadows would be
   both a distraction and a real compositing cost.

   border-color matters as much as the glow: .feed-post was
   1px solid transparent and only odd rows carried a background,
   so half the cards had no edge at all.

   My Signal is unaffected -- .feed-post.sig-framed is (0,2,0)
   against this rule's (0,1,0), so the gold still wins there.
   ============================================================ */

.feed-post { --card-pad: 16px; padding: var(--card-pad); border-radius: 12px; margin-bottom: 6px; animation: postIn .3s ease; transition: background .2s; border: 1px solid transparent; box-sizing: border-box; width: 100%; cursor: pointer; }
/* MUST sit after the base .feed-post rule above: that rule ends with the
   border shorthand (1px solid transparent), which resets border-color at
   equal specificity, so declaring the accent edge earlier in the file was
   silently discarded on odd rows. Source order decides equal-specificity
   ties. */
.feed-post { box-shadow: 0 0 22px rgba(var(--ion-rgb), .13), inset 0 0 10px rgba(var(--ion-rgb), .05); border-color: rgba(var(--ion-rgb), .13); }
/* border-color deliberately NOT set here any more: this selector is
   (0,2,0) and was silently beating the accent border on .feed-post
   (0,1,0), so the skin glow had an edge on even rows only. The zebra
   background stays; the edge is now uniform and skin-driven. */
.feed-post:nth-child(odd) { background: rgba(255,255,255,.04); }
/* A premium user's own posts, subtly -- a colored left edge keyed to
   their real accent color, not a loud full-card treatment. padding-left
   drops 2px to match the border growing from the shared 1px to 3px, so
   content doesn't visibly shift versus every other post. */
.feed-post-own-accent { border-left: 3px solid var(--post-accent, var(--ion)); padding-left: 14px; }
.feed-post:nth-child(even) { background: rgba(var(--ion-rgb),.09); border-color: rgba(var(--ion-rgb),.1); }
.feed-post:hover { background: rgba(var(--ion-rgb),.14); }
.feed-post.highlight { animation: highlightPulse 1.8s ease; }
@keyframes highlightPulse { 0%,100% { box-shadow: none; } 30% { box-shadow: 0 0 0 2px var(--ion); } }
@keyframes postIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
/* Unified header row: avatar + name/time/tag, always together.
   Body content indents to align under it on desktop, and drops that
   indent entirely on mobile so nothing is wasted. */
.post-header-row { display: flex; align-items: center; gap: 10px; min-width: 0; }
.post-head-info { flex: 1; min-width: 0; }
.post-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.post-top-actions { display: none; gap: 2px; flex-shrink: 0; cursor: default; }
.feed-post.expanded .post-top-actions { display: flex; }
.post-username { color: var(--text); text-decoration: none; } /* size/weight: site.css username scale */
.post-username:hover { color: var(--star); }
.post-time { color: var(--text-dimmer); font-family: var(--mono); font-size: .75rem; }
.edited-tag { color: var(--text-dimmer); font-family: var(--mono); font-size: .68rem; font-style: italic; }
.post-category-tag { font-family: var(--mono); font-size: .65rem; letter-spacing: .04em; text-transform: uppercase; color: var(--star); background: rgba(var(--ion-rgb),.1); border: 1px solid var(--line); border-radius: 10px; padding: 2px 8px; text-decoration: none; }
.post-category-tag:hover { background: rgba(var(--ion-rgb),.2); }
/* One gutter value, applied to BOTH sides. The left inset exists to clear
   the avatar column; the right had none, so content sat 63px from the left
   card edge and 17px from the right and every post fell flat against its
   right side. Same number both sides means they cannot drift, and the
   mobile case below is one variable change rather than two padding rules
   that have to be remembered together. */
.feed-post { --post-gutter: 46px; }
.post-body { padding-left: var(--post-gutter); padding-right: var(--post-gutter); margin-top: 8px; }
@media (max-width: 600px) {
  .feed-post { --post-gutter: 0px; }

  /* ============================================================
     EDGE TO EDGE. One mobile language across every feed surface --
     game feeds and My Signal alike.
     ------------------------------------------------------------
     The card cancels its wrapper's own side padding with a negative
     margin, so it meets both screen edges while every OTHER child of
     that wrapper (the game strip, the composer, the tools bar) keeps
     its margin. --edge-pad is declared by the wrapper, so Feed's 20px
     and My Signal's 14px each undo exactly their own value and neither
     has to know the other's number.

     Was asymmetric before, which is what made it read as broken: the
     card had margin on the left and was clipped on the right.

     Radius and side borders go with it. A rounded corner needs
     something to sit against, and against a screen edge it just looks
     like a rendering fault. Top and bottom borders stay, because they
     are what separates one card from the next once the sides are gone.

     The glow stays and now does real work: with no side edges it reads
     along the TOP and BOTTOM of each card, which is the separation you
     scroll past. Gap widened 6px -> 14px so that glow has room to be
     seen rather than being crushed between two cards.
     ============================================================ */
  .feed-post {
    /* width:auto is the load-bearing line, not the margins. The base rule
       sets width:100%, which pins the card to its container -- so negative
       margins only SHIFT it left and it reaches one edge while stopping
       40px short of the other. Measured: left 0, right 340 in a 380px
       viewport. Releasing the width lets the negative margins actually
       widen it to meet both. */
    width: auto;
    margin-left: calc(var(--edge-pad, 0px) * -1);
    margin-right: calc(var(--edge-pad, 0px) * -1);
    margin-bottom: 14px;
    border-radius: 0;
    border-left: 0;
    border-right: 0;
  }
}
/* Text is capped at a readable measure while images keep the full card
   width. Widening the column is for the screenshots -- this community's
   primary content -- but at 908px a text post would run 95-105
   characters per line, which is worse to read than it was before. 64ch
   keeps prose at a comfortable measure and lets media have the room. */
.post-content { color: var(--text); font-size: .96rem; line-height: 1.55; white-space: pre-wrap; overflow-wrap: anywhere; margin-bottom: 10px; max-width: 64ch; }
.mention-tag { color: var(--star); font-weight: 600; text-decoration: none; }
.mention-tag:hover { text-decoration: underline; }
/* touch-callout:none -- a hold on the image is now also a valid react
   trigger (site.js's press-and-hold covers the whole post, "anywhere"
   includes the image), so the OS's own long-press save-image menu is
   suppressed the same way it already is on the admin game-card grid. */
.post-image, .comment-image { max-width: 100%; max-height: 420px; border-radius: 12px; border: 1px solid var(--line); display: block; margin-bottom: 10px; object-fit: cover; -webkit-touch-callout: none; }
/* The 420px cap above was sized for the old 640px column, where a 16:9
   screenshot rendered 360px tall and comfortably filled the width. At the
   current 926px of content width that same image wants to be 521px tall,
   so the cap was shrinking it to 745px and leaving a 136px dead strip
   down the right of every card -- measured, not guessed.
   560px lets 16:9 (the overwhelmingly common case for game screenshots)
   fill the column edge to edge. Deliberately NOT width:100%: that would
   force portrait shots to crop to a horizontal band. They stay their own
   shape and simply centre instead of hugging the left edge. */
.post-image { max-height: 560px; margin-left: auto; margin-right: auto; }
.comment-image { max-height: 160px; margin-top: 6px; margin-bottom: 0; border-radius: 4px; }
/* justify-content:flex-end moves the react + comment-count buttons to
   the post's right edge, per explicit request. */
.post-action-row { display: flex; align-items: center; justify-content: flex-end; gap: 4px; cursor: default; }
.action-btn { background: none; border: none; cursor: pointer; padding: 5px 8px; display: flex; align-items: center; gap: 5px; border-radius: 8px; color: var(--text-dimmer); transition: all .2s; font-size: .82rem; font-family: var(--body); }
.action-btn:hover { background: rgba(12,19,34,.5); color: var(--star); }
.action-btn.reacted { color: var(--star); background: rgba(var(--ion-rgb),.12); }
.action-btn.danger:hover { color: #ff9baa; }
.comments-section { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .35s ease; cursor: default; }
.comments-section.open { grid-template-rows: 1fr; }
.comments-section-inner { overflow: hidden; padding-left: 46px; min-height: 0; }
@media (max-width: 600px) { .comments-section-inner { padding-left: 6px; } }
.comments-list { display: flex; flex-direction: column; gap: 10px; margin: 10px 0; }
.comment-item { display: flex; gap: 8px; min-width: 0; }
.comment-body { flex: 1; min-width: 0; background: rgba(5,7,13,.4); border: 1px solid var(--line-soft); border-radius: 2px; padding: 7px 11px; position: relative; }
.comment-head { display: flex; align-items: baseline; gap: 6px; margin-bottom: 2px; flex-wrap: wrap; }
.comment-username { color: var(--text); text-decoration: none; } /* size/weight: site.css username scale */
.comment-username:hover { color: var(--star); }
.comment-time { color: var(--text-dimmer); font-family: var(--mono); font-size: .66rem; }
.comment-top-actions { display: flex; align-items: center; gap: 2px; margin-left: auto; }
.comment-top-actions button { background: none; border: none; color: var(--text-dimmer); cursor: pointer; padding: 2px; display: flex; }
.comment-top-actions button:hover { color: var(--star); }
.comment-top-actions button.danger:hover { color: #ff9baa; }
.comment-top-actions svg { width: 13px; height: 13px; }
.comment-content { font-size: .85rem; color: var(--text-dim); line-height: 1.45; white-space: pre-wrap; overflow-wrap: anywhere; }
.comment-actions { display: flex; align-items: center; gap: 4px; margin-top: 4px; }
.comment-actions .action-btn { padding: 3px 6px; font-size: .72rem; }
.comment-form { display: flex; gap: 8px; align-items: center; margin-top: 4px; }
/* The reply field's right padding is DERIVED from the icon cluster, not
   typed as a number, because typing it as a number is what broke it. The
   icons are absolutely positioned, so the input cannot see them and the
   padding is the only thing keeping text out from under them -- it was
   68px, sized back when there were two icons, and the GIF button was added
   later without anyone updating it. Measured on the live Feed: the cluster
   needs 86px, so 18px of every reply ran underneath the icons. Confirmed
   from a real phone screenshot first, then measured.

   Change --ci-count when an icon is added or removed and the padding
   follows. Leave it stale and the bug comes straight back. */
.comment-input-wrap { position: relative; flex: 1; min-width: 0; --ci-count: 3; --ci-size: 24px; --ci-gap: 4px; --ci-inset: 6px; --ci-breathe: 6px; }
.comment-input-wrap input[type=text] { width: 100%; box-sizing: border-box; background: rgba(5,7,13,.55); border: 1px solid var(--line); border-radius: 2px; padding: 8px calc(var(--ci-inset) + (var(--ci-count) * var(--ci-size)) + ((var(--ci-count) - 1) * var(--ci-gap)) + var(--ci-breathe)) 8px 12px; color: var(--text); font-family: var(--body); font-size: .83rem; outline: none; transition: border-color .2s; }
.comment-input-wrap input[type=text]:focus { border-color: var(--ion); }
.comment-input-icons { position: absolute; right: 6px; top: 50%; transform: translateY(-50%); display: flex; align-items: center; gap: 4px; }
.comment-input-icons .emoji-trigger-btn, .comment-input-icons .comment-attach-label { width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; border-radius: 5px; }
.comment-input-icons .emoji-trigger-btn:hover, .comment-input-icons .comment-attach-label:hover { background: rgba(var(--ion-rgb),.12); }
.comment-input-icons svg { width: 20px; height: 20px; }
.comment-attach-label { cursor: pointer; color: var(--text-dimmer); transition: color .2s; }
.comment-attach-label:hover { color: var(--star); }
.comment-form > button[type=submit] { background: var(--ion); color: #03060d; border: none; border-radius: 2px; padding: 8px 14px; font-size: .78rem; font-weight: 600; cursor: pointer; flex-shrink: 0; font-family: var(--body); }
.comment-login-note { font-size: .78rem; color: var(--text-dimmer); margin: 6px 0; }
.comment-login-note a { color: var(--star); }
.comment-preview-wrap { position: relative; display: inline-block; margin-bottom: 6px; max-width: 100%; }
.comment-preview-wrap img { max-height: 90px; max-width: 100%; border-radius: 2px; display: block; }
.comment-preview-wrap button { position: absolute; top: 4px; right: 4px; width: 18px; height: 18px; border-radius: 50%; background: rgba(5,7,13,.85); border: 1px solid var(--line); color: #fff; cursor: pointer; font-size: 11px; line-height: 1; }
.mention-dropdown { position: fixed; background: var(--panel-2); border: 1px solid var(--line); border-radius: 8px; box-shadow: 0 10px 30px rgba(0,0,0,.5); z-index: var(--z-dropdown); min-width: 160px; overflow: hidden; max-height: 220px; overflow-y: auto; }
.mention-dropdown-item { padding: 8px 12px; font-size: .85rem; color: var(--star); cursor: pointer; }
.mention-dropdown-item:hover { background: rgba(var(--ion-rgb),.15); }
.edit-form { display: flex; flex-direction: column; gap: 8px; }
.edit-form textarea { width: 100%; box-sizing: border-box; background: rgba(5,7,13,.55); border: 1px solid var(--ion); border-radius: 8px; padding: 10px 12px; color: var(--text); font-family: var(--body); font-size: .9rem; resize: vertical; min-height: 60px; }
.edit-form-actions { display: flex; gap: 8px; justify-content: flex-end; }
.edit-form-actions button { border: none; border-radius: 6px; padding: 6px 14px; font-size: .78rem; font-weight: 600; cursor: pointer; font-family: var(--body); }
.edit-save-btn { background: var(--ion); color: #03060d; }
.edit-cancel-btn { background: rgba(255,255,255,.06); color: var(--text-dim); }

/* ================================================================
   MEDIA FULL BLEED INSIDE THE CARD -- the Instagram/Facebook treatment.
   The card reached both screen edges but its media did not: an image sat
   inside the card's own 16px padding, so a full-bleed card still showed a
   16px letterbox down each side of the photo. Confirmed from a real device.

   Text padding deliberately STAYS. Body copy running into the bezel is
   uncomfortable to read and is not what full bleed is for -- only media
   breaks out.

   The break-out is expressed against --card-pad rather than a literal 16px,
   so the media and the padding it cancels can never drift apart. That
   mattered here: the card's padding is one declaration and this is another
   file, exactly the shape of thing that goes stale silently.

   THIS BLOCK IS AT THE END OF THE FILE ON PURPOSE. .post-image declares
   margin-left/right:auto further up at the same (0,1,0) weight, and media
   queries add no specificity -- placed any earlier these rules would simply
   lose to it. That trap has already cost this session three separate
   debugging rounds, so this one is not being re-litigated.

   The GIF needs no width rule here. axiomGifCapW measures the MOUNT to
   choose its width, and the mount is what goes full bleed, so the GIF
   follows automatically -- one source of truth for that number rather than
   a CSS rule racing an inline style it cannot beat anyway. */
@media (max-width: 600px) {
  .post-image, .axiom-gif-mount {
    margin-left: calc(var(--card-pad) * -1);
    margin-right: calc(var(--card-pad) * -1);
    width: calc(100% + (var(--card-pad) * 2));
    max-width: none;
    border-radius: 0;
  }
  /* Square at the screen edge, and no side rules where there is no longer a
     background for them to sit against. */
  .post-image { border-left: 0; border-right: 0; }
  .feed-post .axiom-gif-wrap { border-radius: 0; }
}
