/**
* SeekQuarry/Yioop --
* Open Source Pure PHP Search Engine, Crawler, and Indexer
*
* Copyright (C) 2009 - 2026 Chris Pollett chris@pollett.org
*
* LICENSE:
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* END LICENSE
*
* @author Chris Pollett
* @license https://www.gnu.org/licenses/ GPL3
* @link https://www.seekquarry.com/
* @copyright 2009 - 2026
* @filesource
*/
/*
messages.css
Stylesheet for User messages
*/
.messages-container,
.mail-container
{
/* a content panel sits on the page in the foreground color, the same
as the rest of the site's content areas, rather than showing the
page background color through it */
background-color: var(--color-foreground);
border: 1px solid #CDE;
display: flex;
height: 42rem;
/* the top nav bar is a 65px fixed .top-container; keep the
container's top border a few px clear of it */
margin-top: 72px;
width: 100%;
}
.contacts-arrow
{
color: var(--gray-50);
font-size: 1.6667rem;
padding: 10px;
text-decoration: none;
}
.user-contacts,
.user-accounts
{
overflow-y: scroll;
overflow-x: auto;
width: 28%;
/* floor wide enough for the full six-icon socialcontrols row
(six ~38px buttons + gaps, the reserved scrollbar, and the
wrap padding) plus headroom so it never wraps on desktop;
28% still applies once the viewport is wider, and the
split-adjuster can resize from here */
min-width: 18.6rem;
}
.socialcontrols-wrap
{
margin: 9px auto 5px auto;
padding: 0 4px;
box-sizing: border-box;
max-width: 100%;
}
.contact-detail
{
border: 1px solid #CDE;
width: 100%;
}
.selected-contact
{
background: #F5F5DC;
}
.contact-detail-inner
{
padding: 10px;
position: relative;
}
.contact-detail a,
.contact-detail span
{
color: var(--gray-0);
font-size: var(--font-size-6);
position: absolute;
text-decoration: none;
top: 17px;
}
/* A contact row places its parts by hand, so the name and the call
controls are wrapped together and that wrapper is what gets placed.
Inside it they lay out in a line, which keeps the buttons reachable
however long the name is, and the right edge stops the line running
under the row's own menu. */
.html-ltr .contact-detail span.contact-detail-line,
.html-rtl .contact-detail span.contact-detail-line
{
align-items: center;
display: flex;
gap: 0.5rem;
inset-inline-end: 2.75rem;
inset-inline-start: 60px;
top: 17px;
}
.contact-detail span.contact-detail-line a,
.contact-detail span.contact-detail-line span
{
inset-inline-end: auto;
inset-inline-start: auto;
position: static;
top: auto;
}
.contact-detail span.contact-detail-line a#contact-id-,
.contact-detail span.contact-detail-line > a
{
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.contact-detail span.contact-detail-line span.contact-ringing-answer
{
align-items: center;
display: inline-flex;
flex: 0 0 auto;
gap: 4px;
}
.html-ltr .contact-detail span.contact-ringing-answer div,
.html-rtl .contact-detail span.contact-ringing-answer div,
.html-ltr .contact-detail span.contact-ringing-answer a,
.html-rtl .contact-detail span.contact-ringing-answer a
{
left: auto;
position: static;
right: auto;
top: auto;
}
.contact-detail-request
{
margin-left:5px;
margin-right:5px;
position:relative;
top:-45px;
}
/* A contact's row lays its parts out along a line, so the marks that
open what can be done stand at the end of the row itself rather than
dropping below it. The name inside the row is placed by hand, so the
part holding it is given the space left over. */
.contact-detail
{
align-items: center;
display: flex;
}
.contact-detail .contact-detail-inner
{
flex: 1;
min-width: 0;
}
.contact-detail .unread-messages
{
flex: none;
order: 1;
}
/* What can be done to a conversation sits behind three marks standing
one above the other, at the end of the row. The marks are drawn
rather than written as a letter, since a letter for them is missing
from some of the faces a browser has to hand. */
.contact-detail-options
{
flex: none;
margin-left: 5px;
margin-right: 5px;
order: 2;
}
.contact-detail-options ul.link-dropdown > li
{
border: 0;
}
/* A contact's name is placed by hand inside the row, and the rule
that does it would take the words in this list with it, leaving
them a few pixels tall, so they are set back in the ordinary flow. */
.contact-detail-options a,
.contact-detail-options span
{
font-size: var(--font-size-7);
position: static;
top: auto;
}
/* The list of what can be done stands clear of the row and wide
enough to read, since the panel it opens in is a narrow one. */
.contact-options ul.link-dropdown li ul
{
background-color: var(--color-background);
inset-inline-end: 0;
min-width: 11rem;
z-index: 6;
}
.contact-options ul.link-dropdown li ul li
{
float: none;
text-align: start;
white-space: nowrap;
}
.contact-options-dots
{
align-items: center;
background: none;
display: flex;
flex-direction: column;
justify-content: center;
padding: 4px 8px;
row-gap: 3px;
text-decoration: none;
}
.contact-options-dots span
{
background-color: var(--gray-33);
border-radius: 50%;
display: block;
height: 4px;
width: 4px;
}
.contact-options-dots:hover span
{
background-color: var(--gray-0);
}
.html-ltr .contact-detail a,
.html-ltr .contact-detail span,
.html-rtl .contact-detail a,
.html-rtl .contact-detail span
{
inset-inline-start: 60px;
}
.contact-detail a:hover
{
text-decoration: underline;
}
.contact-detail:hover
{
background: #E9F5DC;
}
.contact-actions button
{
display: none;
}
/* The box and the mark beside it share one line, the box taking what
room is left over so the mark keeps its own width at any screen
size. Without this the box took the whole width and pushed the mark
onto a line of its own. */
.contact-actions
{
align-items: center;
display: flex;
flex-wrap: nowrap;
}
.contact-actions input
{
border-radius: 5px;
flex: 1 1 auto;
font-size: 2rem;
font-weight: bold;
margin: 4px;
min-width: 0;
}
/* The mark matches the box it stands beside. The box is set larger
than ordinary text, so the mark takes the same text size and the same
room above and below rather than a height of its own, which held it
at a third of the box whatever the text size was. */
.contact-actions:focus-within button
{
background-color: #F0F0F6;
border:1px solid var(--gray-50);
border-radius:5px;
color:var(--gray-0);
display: inline-block;
font-size: 2rem;
font-weight:bold;
margin: 4px;
padding: 1px 0.5rem;
vertical-align: middle;
}
.split-adjuster
{
background-color: #CDE;
cursor: ew-resize;
height: 100%;
width: 2px;
}
/* The conversation and the row a message is written in stand in one
column of a settled height, so the row keeps its place at the foot and
the conversation above it scrolls. Without this the row grew downward
past the border of the chat as a message got longer. */
.contact-messages,
.account-messages
{
display: flex;
flex: 1;
flex-direction: column;
min-height: 0;
min-width: 0;
}
.contact-messages > #conversation,
.account-messages > #conversation
{
flex: 1 1 auto;
min-height: 0;
overflow-y: auto;
}
/* With no conversation yet the warning stands where the message list
would, so it takes the same growing space and the box to write in
stays at the foot of the panel rather than floating up under a short
warning. */
/* With a conversation open but no messages yet, the writing box sits
below the notice, so the notice takes the growing space and the box
stays at the foot. Where no box follows -- nothing picked, or a
contact that is gone -- the notice keeps its own size and sits as a
small centered card rather than a tall panel. */
.contact-messages > .user-messages-warning:has(~ #new-message-container)
{
flex: 1 1 auto;
min-height: 0;
}
#new-message-container
{
flex: none;
}
/* A message stands in its own space so the mark that takes it back can
sit at its corner. The mark is a ring with a cross in it, kept faint
until the message is under the pointer. */
.message-detail
{
position: relative;
}
.message-delete
{
align-items: center;
background-color: var(--color-background);
border: 1px solid var(--gray-67);
border-radius: 50%;
color: var(--gray-33);
display: flex;
font-size: 0.7rem;
height: 1.1rem;
inset-block-start: -0.55rem;
inset-inline-end: -0.55rem;
justify-content: center;
line-height: 1;
opacity: 0;
position: absolute;
text-decoration: none;
transition: opacity 0.15s;
width: 1.1rem;
}
.message-detail:hover .message-delete,
.message-delete:focus
{
opacity: 1;
}
.message-delete:hover
{
background-color: var(--gray-67);
color: var(--color-background);
}
.selected-conversation-header
{
background-color: #FED;
height: 50px;
width: 100%;
}
.selected-conversation-header .call-controls
{
min-width: 70px;
padding-left:10px;
padding-right:10px;
position: relative;
top:3px;
}
.selected-conversation-header h2
{
padding-left:20px;
padding-right:20px;
position: relative;
top:5px;
width: fit-content;
}
.selected-conversation-header h2 span
{
position: relative;
top:-10px;
}
.conversation
{
display: flex;
flex-direction: column-reverse;
height: calc(100% - 110px);
overflow-y: scroll;
}
#av-call
{
background: var(--gray-0);
color: var(--gray-100);
display: none;
height: calc(100% - 49px);
}
#av-call-div
{
background: var(--gray-100);
border-radius: 20%;
font-size: 6rem;
height: 8.4rem;
left: 55%;
padding-top: 15px;
position: absolute;
text-align: center;
top: 40%;
width: 9rem;
}
#local-av
{
display: block;
height: 20%;
position: relative;
left: 80%;
top: -8px;
width: 20%;
z-index: 20;
}
#remote-av
{
display:block;
margin:0;
padding: 0;
position: relative;
top: calc(-13% - 8px);
width: 100%;
z-index: 1;
}
.video-call
{
background-color: #8FA;
}
.selected-conversation-header .call-controls .icon-button-container
{
height: var(--control-height);
padding: 0;
width: var(--control-height);
}
#video-end
{
display: none;
}
.video-call-end
{
background-color: #F55;
}
.message-detail-outer
{
display: inline-block;
max-width: 50%;
overflow-wrap: anywhere;
text-align: justify;
}
.message-detail-outer img
{
max-width: 100%;
}
.message-icon
{
border: 2px solid lightblue;
border-radius: 17px;
width:35px;
}
.message-other-time,
.message-user-time
{
color: var(--gray-73);
font-size: var(--font-size-1);
}
.html-ltr .message-user-time,
.html-rtl .message-user-time
{
margin-inline-end:3rem;
}
.html-ltr .message-other-time,
.html-rtl .message-other-time
{
margin-inline-start:0.48rem;
}
.message-detail
{
padding: 7px;
margin: 5px 3rem 5px 3rem;
width: fit-content;
}
.message-detail.message-mine
{
background-color: var(--color-topbar);
border: 1px solid var(--gray-80);
color: var(--gray-0);
}
.message-detail.message-theirs
{
background-color: var(--gray-93);
border: 1px solid var(--gray-80);
color: var(--gray-0);
}
/* The square corner is on the bottom on the side the message is anchored
to, which flips with direction, so the corners are named logically:
the block-end inline-end corner is the squared one. */
.html-ltr .message-detail,
.html-rtl .message-detail
{
border-start-start-radius: 7px;
border-start-end-radius: 7px;
border-end-end-radius: 0;
border-end-start-radius: 7px;
}
/* The squared corner is the block-start inline-start one, which flips
with direction; named logically it is one rule for both. */
.html-ltr .top-corner,
.html-rtl .top-corner
{
border-start-start-radius: 0;
border-start-end-radius: 7px;
border-end-end-radius: 7px;
border-end-start-radius: 7px;
}
.user-messages-warning,
.user-mail-warning
{
background-color: #FED;
border-color: #FEB;
border: 1px solid transparent;
border-radius: 5px;
color: #860;
/* the panel is a flex column, so auto margins center the notice
both ways; it sits in the middle of the pane rather than low
down, which a downward offset had made it do */
margin: auto;
padding: 10px;
text-align: center;
width: fit-content;
}
#new-message-container
{
box-sizing: border-box;
left: 0;
margin:auto;
position: relative;
text-align: center;
width: 100%;
}
#new-message
{
background-color: transparent;
border: none;
box-sizing: border-box;
color: var(--gray-0);
flex: 1;
font-family: inherit;
font-size: var(--font-size-7);
line-height: 1.3;
max-height: 6rem;
min-width: 0;
outline: none;
overflow-y: auto;
padding: 6px 0;
resize: none;
}
#attach-new-file
{
align-items: center;
background-color: var(--color-topbar);
border-radius: 50%;
border: solid 1px var(--gray-50);
color: var(--gray-0);
cursor: pointer;
display: inline-flex;
flex: none;
font-size: 28px;
font-weight: bold;
height: 44px;
justify-content: center;
line-height: 1;
text-decoration: none;
width: 44px;
}
#send-message
{
background-color: transparent;
border: none;
color: var(--gray-0);
cursor: pointer;
font-size: 22px;
height: 40px;
padding: 0 4px;
}
#contact-filter
{
width: 11.4rem;
}
#user-input-container
{
align-items: flex-end;
background-color: var(--gray-93);
border-radius: 20px;
border: solid 1px var(--gray-80);
color: var(--gray-0);
display: inline-flex;
flex: 1;
gap: 2px;
min-height: 44px;
min-width: 0;
padding-block: 2px;
padding-inline: 6px;
text-align: start;
}
#emoji-picker-button
{
background-color: transparent;
border: none;
cursor: pointer;
font-size: 22px;
height: 40px;
padding: 0 4px;
}
.emoji-picker-container
{
display: none;
border: solid 1px var(--gray-50);
border-radius: 5px;
width: 400px;
position: absolute;
bottom: 105%;
height: 350px;
background-color: var(--gray-100);
z-index: 1;
}
.emoji
{
border: none;
display: inline-block;
font-size: 22px;
cursor: pointer;
font-size: 35px;
padding: 2px;
margin: 2px;
}
.emoji-group-navigator
{
position: absolute;
bottom: 0;
height: 15%;
border-top: 1px solid var(--gray-50);
text-align: center;
z-index: 2;
width: 100%;
}
.emoji-group-container
{
display: none;
text-align: left;
height: 85%;
position: absolute;
width: 100%;
}
.emoji-container
{
width: 100%;
height: 90%;
overflow-y: scroll;
position: relative;
padding: 10px;
}
#emoji-group-name
{
font-size: 10px;
text-transform: uppercase;
color: var(--gray-50);
margin: 5px;
}
.active-emoji-group
{
display: flex;
flex-direction: column;
}
.emoji-group-tab
{
margin: 5px;
font-size: 22px;
cursor: pointer;
display: inline-block;
padding: 5px;
}
.active-emoji-group-tab
{
background-color: gainsboro;
border: 1px solid var(--gray-50);
border-radius: 10%;
}
#new-message-container form {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
justify-content: center;
}
.audio-control {
background-color: transparent;
border: none;
color: var(--gray-0);
cursor: pointer;
font-size: 22px;
height: 40px;
padding: 0 4px;
}
#audio-controls {
display: inline-flex;
gap: 4px;
align-items: center;
}
#audio-controls.none {
display: none;
}
#mic-icon,
#play-audio-btn,
#delete-audio-btn {
align-items: center;
display: inline-flex;
justify-content: center;
}
.recording {
color: red;
}
.ai-summary-hidden {
display: none;
margin-left: 65px;
}
/* Audio transcription styles */
.audio-message-container {
display: flex;
flex-direction: column;
gap: 4px;
padding: 8px;
background-color: #f8f9fa;
border-radius: 8px;
margin: 8px 0;
width: 300px;
max-width: 100%;
}
.audio-player-wrapper {
position: relative;
width: 100%;
margin-bottom: -4px;
}
.audio-message-container audio,
.audio-message-container video {
width: 100%;
border-radius: 4px;
}
/* The mark that shows what was said sits at the lower corner of the
recording it belongs to, over the picture the recording has nothing
to fill, where it is out of the way of the play controls. */
.transcribe-btn {
position: absolute;
bottom: 6px;
inset-inline-end: 6px;
background-color: transparent;
border: none;
color: var(--gray-0);
cursor: pointer;
font-size: 16px;
padding: 2px;
height: auto;
width: auto;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
transition: all 0.2s;
z-index: 1;
text-shadow: 0 0 3px rgba(255, 255, 255, 0.8);
}
.transcribe-btn:hover {
transform: scale(1.2);
text-shadow: 0 0 5px rgba(255, 255, 255, 1);
}
.transcribe-btn:active {
transform: scale(0.95);
}
.transcript-container {
display: none;
padding: 8px;
background-color: var(--gray-100);
border: 1px solid var(--gray-88);
border-radius: 4px;
white-space: normal;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-word;
font-size: 14px;
color: var(--gray-20);
line-height: 1.4;
max-height: 200px;
overflow-y: auto;
word-spacing: -0.1em;
}
.transcript-container.show {
display: block;
}
.transcript-container.loading {
color: var(--gray-40);
font-style: italic;
}
.transcript-container.error {
color: #d32f2f;
font-style: italic;
}
@media (max-width: 600px)
{
.inner-bar h1 span
{
top: -15px;
}
.sub-top-container
{
margin-top: 3rem;
}
.messages-container, .mail-container
{
height: 30rem;
margin-top: 66px;
}
.user-contacts, .user-accounts
{
width: 100%;
min-width: 0;
}
.contact-actions:focus-within button
{
font-size: 2rem;
height: unset;
z-index: 1000;
}
.selected-conversation-header h2
{
min-width: 200px;
overflow: hidden;
text-wrap: nowrap;
}
#av-call-div
{
left: 33%;
top: 37%;
}
#local-av
{
float: right;
left: initial;
right:5px;
top: 0;
}
.message-detail-outer
{
display: inline-block;
max-width: 90%;
overflow-wrap: anywhere;
text-align: justify;
}
#contact-filter
{
width: 340px;
}
.emoji-picker-container
{
display: none;
border: solid 1px var(--gray-50);
border-radius: 5px;
width: 250px;
position: absolute;
bottom: 105%;
height: 300px;
background-color: var(--gray-100);
z-index: 1;
}
.emoji-group-tab
{
margin: 2% 0 0 0;
font-size: 100%;
display: inline-block;
padding: 2%;
}
.messages-container.no-selection .contact-messages
{
display: none;
}
.messages-container.has-selection .user-contacts
{
display: none;
}
.user-contacts,
.contact-messages
{
width: 100%;
}
}
/* How lately a contact was at their own messages screen is shown by the
border around their icon, greenest when they are there now and cooling
through yellow and orange to red as the last sighting recedes. Color
alone cannot carry the fact, so the icon also says in words how lately
they were seen, which the alt text and the title both hold, and the
border thickness falls with each step for a reader who cannot tell
those colors apart. A contact not seen at all keeps the plain icon. */
.message-icon.contact-seen-here
{
border: 4px solid light-dark(#177245, #4ade80);
}
.message-icon.contact-seen-lately
{
border: 3px solid light-dark(#ca8a04, #fde047);
}
.message-icon.contact-seen-a-while
{
border: 3px solid light-dark(#ea580c, #fb923c);
}
.message-icon.contact-seen-long
{
border: 2px solid light-dark(#b91c1c, #f87171);
}
/* The words and buttons for a call are drawn for every contact and
kept out of the way until that contact calls, so a call arriving on
an open screen needs no markup built in the browser and the controls
keep the place the page gives them. */
.call-trouble
{
color: light-dark(#b91c1c, #f87171);
font-weight: bold;
margin: 8px 0;
text-align: center;
}
/* A contact whose call is waiting has their name pulse, and the two
buttons for picking the call up or turning it down stand beside it. */
.contact-ringing
{
animation: contact-ringing-pulse 1.2s ease-in-out infinite;
font-weight: bold;
}
@keyframes contact-ringing-pulse
{
0%, 100%
{
opacity: 1;
}
50%
{
opacity: 0.35;
}
}
/* What the pulse means, said in words, kept out of sight but left where
a screen reader still reads it. */
.contact-ringing-said
{
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
.contact-ringing-said.hidden-until-ringing,
.contact-ringing-answer.hidden-until-ringing,
.html-ltr .contact-detail span.contact-ringing-answer.hidden-until-ringing,
.html-rtl .contact-detail span.contact-ringing-answer.hidden-until-ringing
{
display: none;
}
.contact-ringing-answer
{
display: inline-flex;
gap: 4px;
margin-left: 8px;
vertical-align: middle;
}
/* Where someone has asked for less movement the pulse is not run;
the bold weight and the words beside it still say a call waits. */
@media (prefers-reduced-motion: reduce)
{
.contact-ringing
{
animation: none;
}
}